This commit is contained in:
CortexCore
2024-03-31 23:31:00 +08:00
parent e179d2eb53
commit b7b89ee71a
641 changed files with 31286 additions and 22134 deletions

View File

@@ -2,17 +2,81 @@ using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem.Composites;
using UnityEngine.UIElements;
namespace BITKit.UX
{
[System.Serializable]
[Serializable]
public record AlertMessage
{
public string title = "Alert";
public string message = "message";
public Action OnConfirm;
public Action<bool> OnChoice;
}
[Serializable]
public sealed class UXAlertPopup : IUXPopup
{
public void Popup(string content, float duration = 3f)
{
Alert.Print(new AlertMessage()
{
title = "Alert",
message = content
});
}
}
[Serializable]
public sealed class RequestOpenUrl : BITAppForUnity.OpenUrl
{
[SerializeReference,SubclassSelector] private AlertMessage alertMessage;
public override void Execute()
{
Alert.Print(alertMessage with
{
OnConfirm = base.Execute
});
}
}
public struct UnityDialogue : IUXDialogue
{
public void Show(string content, string title = "Alert", Action confirmAction = null, Action<bool> onChoose = null)
{
switch (confirmAction,onChoose)
{
case (null, null):
Alert.Print(title, content);
break;
case (not null, null):
Alert.Print(new AlertMessage()
{
title = title,
message = content,
OnConfirm = confirmAction
});
break;
case (null, not null):
Alert.Print(new AlertMessage()
{
title = title,
message = content,
OnChoice = onChoose
});
break;
case (not null, not null):
Alert.Print(new AlertMessage()
{
title = title,
message = content,
OnConfirm = confirmAction,
OnChoice = onChoose
});
break;
}
}
}
public static class Alert
{
public static void Print(AlertMessage message)
@@ -33,28 +97,59 @@ namespace BITKit.UX
{
internal static UXAlert Singleton;
[SerializeField] private UIDocument document;
Label titleLabel;
Label contextLabel;
Button comfirmButton;
private void Awake()
[UXBindPath(UXConstant.TitleLabel)]
private Label _titleLabel;
[UXBindPath(UXConstant.ContextLabel)]
private TextElement _contextLabel;
[UXBindPath(UXConstant.MainButton)]
private Button _comfirmButton;
[UXBindPath(UXConstant.SecButton)]
private Button _cancelButton;
private void Start()
{
DI.Register<IUXDialogue,UnityDialogue>();
Singleton = this;
var container = document.rootVisualElement;
titleLabel = container.Q<Label>(UXConstant.TitleLabel);
contextLabel = container.Q<Label>(UXConstant.ContextLabel);
comfirmButton = container.Q<Button>(UXConstant.MainButton);
comfirmButton.clicked += Close;
UXUtils.Inject(this);
Close();
}
internal void PrintAlertMessage(AlertMessage message)
{
document.rootVisualElement.SetActive(true);
titleLabel.text = message.title;
contextLabel.text = message.message;
_titleLabel.text = message.title;
_contextLabel.text = message.message;
_comfirmButton.clicked += Close;
_cancelButton.clicked += Close;
_cancelButton.SetActive(true);
if (message.OnConfirm is not null)
{
_comfirmButton.clicked += message.OnConfirm;
}else if (message.OnChoice is not null)
{
_comfirmButton.clicked += () => message.OnChoice.Invoke(true);
_cancelButton.clicked += () => message.OnChoice.Invoke(false);
}
else
{
_cancelButton.SetActive(false);
}
BITAppForUnity.AllowCursor.AddElement(this);
}
void Close()
private void Close()
{
_comfirmButton.clickable = null;
_cancelButton.clickable = null;
document.rootVisualElement.SetActive(false);
BITAppForUnity.AllowCursor.RemoveElement(this);
}
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 52bef53d9f175424da94066fc9eb38d5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,56 @@
using System;
using System.Collections;
using System.Collections.Generic;
using BITKit.IO;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UIElements;
namespace BITKit.UX
{
public class UXApplicationFile : MonoBehaviour
{
[SerializeReference, SubclassSelector] private IApplicationFile applicationFile;
[UXBindPath("save-button")]
private Button _saveButton;
[UXBindPath("save-as-button")]
private Button _saveAsButton;
[UXBindPath("reload-button")]
private Button _reloadButton;
[UXBindPath("load-button")]
private Button _loadButton;
[UXBindPath("save-path-label")]
private Label _savePathLabel;
private void Start()
{
UXUtils.Inject(this);
_saveButton.clicked += applicationFile.Save;
_loadButton.clicked += ()=>applicationFile.Load();
_saveAsButton.clicked += ()=>applicationFile.SaveAs();
_reloadButton.clicked += applicationFile.Reload;
applicationFile.OnPathChanged+=OnPathChanged;
OnPathChanged(null,applicationFile.Current);
_reloadButton.SetEnabled(false);
}
private async void OnPathChanged(IStorageFile arg1, IStorageFile arg2)
{
await UniTask.SwitchToMainThread();
if (destroyCancellationToken.IsCancellationRequested) return;
if (arg2 is not null)
{
_savePathLabel.text = arg2.GetPath();
_reloadButton.SetEnabled(true);
}
else
{
_reloadButton.SetEnabled(false);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2894e80bc1f4e5f469a6bbfe4e7f3b46
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -16,7 +16,8 @@
"GUID:9400d40641bab5b4a9702f65bf5c6eb5",
"GUID:7d3ace4c6aad3684abe11aa38b6cdf99",
"GUID:517785bb4600a5140b47eac5fa49b8fc",
"GUID:838d3286f0973344ab6e99d3951012f7"
"GUID:838d3286f0973344ab6e99d3951012f7",
"GUID:a11ff146d38b27a44af87b4b4d9c4ecb"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@@ -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();

View File

@@ -2,6 +2,7 @@ using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading;
using Cysharp.Threading.Tasks;
using UnityEngine.UIElements;
#if UNITY_EDITOR
using UnityEditor;
@@ -51,48 +52,32 @@ namespace BITKit.UX
}
public class UXData : MonoBehaviour
{
[SerializeReference, SubclassSelector] public References key;
[SerializeReference, SubclassSelector] public DataComponents dataParser;
bool initialized;
void OnEnable()
[SerializeReference, SubclassSelector] private IReference key;
[SerializeReference, SubclassSelector] private IDataComponent dataParser;
private async void Start()
{
dataParser.SetKey(key);
if (initialized)
{
dataParser.OnStart();
}
await UniTask.NextFrame();
if (destroyCancellationToken.IsCancellationRequested) return;
dataParser.SetKey(key.Value);
dataParser.OnStart();
destroyCancellationToken.Register(dataParser.OnStop);
}
void Start()
{
if (initialized is false)
{
dataParser.OnStart();
initialized = true;
}
}
void OnDisable()
{
if (initialized)
{
dataParser.OnStop();
}
}
#if UNITY_EDITOR
[UnityEditor.CustomEditor(typeof(UXData))]
public class UXDataInspector : BITInspector<UXData>
{
public override VisualElement CreateInspectorGUI()
{
CreateSubTitle(Constant.Header.Settings);
var key = root.Create<PropertyField>();
CreateSubTitle(Constant.Header.Reference);
var dataParser = root.Create<PropertyField>();
key.bindingPath = nameof(UXData.key);
dataParser.bindingPath = nameof(UXData.dataParser);
return root;
}
}
#endif
// #if UNITY_EDITOR
// [UnityEditor.CustomEditor(typeof(UXData))]
// public class UXDataInspector : BITInspector<UXData>
// {
// public override VisualElement CreateInspectorGUI()
// {
// CreateSubTitle(Constant.Header.Settings);
// var key = root.Create<PropertyField>();
// CreateSubTitle(Constant.Header.Reference);
// var dataParser = root.Create<PropertyField>();
//
// key.bindingPath = nameof(UXData.key);
// dataParser.bindingPath = nameof(UXData.dataParser);
// return root;
// }
// }
// #endif
}
}

View File

@@ -71,9 +71,10 @@ namespace BITKit.UX.Components
[System.Serializable]
public sealed class BoolEvent : DataComponents
{
public UnityEvent<bool> outputEvent;
public UnityEvent ifTrueEvent;
public UnityEvent ifFlaseEvent;
[SerializeField]private UnityEvent<bool> outputEvent;
[SerializeField]private UnityEvent ifTrueEvent;
[SerializeField]private UnityEvent ifFlaseEvent;
private readonly Optional<bool> _current = new();
public override void OnStart()
{
Data.AddListener<bool>(key, OnSetBool, true);
@@ -84,6 +85,11 @@ namespace BITKit.UX.Components
}
async void OnSetBool(bool boolean)
{
if (_current.Allow)
{
if (_current.Value == boolean) return;
}
_current.SetValueThenAllow(boolean);
try
{
await UniTask.SwitchToMainThread(BITApp.CancellationToken);

View File

@@ -22,7 +22,7 @@ namespace BITKit.UX
this.visualElement = visualElement;
contextLabel = visualElement.Q<Label>(UXConstant.ContextLabel);
titleLabel = visualElement.Q<Label>(UXConstant.TitleLabel);
numberLabel = visualElement.Q<Label>(UXConstant.TitleLabel);
numberLabel = visualElement.Q<Label>(UXConstant.NumberLabel);
descriptionLabel = visualElement.Q<Label>(UXConstant.DescriptionLabel);
button = visualElement.Q<Button>(UXConstant.MainButton);
secButton = visualElement.Q<Button>(UXConstant.SecButton);
@@ -33,7 +33,15 @@ namespace BITKit.UX
public T Get<T>(int index) 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)) ;
}
}
}
}

View File

@@ -0,0 +1,62 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.UIElements;
namespace BITKit.UX
{
public class UXToolTips : MonoBehaviour
{
private VisualElement root;
private Label label;
private void Start()
{
root = GetComponent<UIDocument>().rootVisualElement;
label = root.Q<Label>();
}
private void Update()
{
var tooltip = CurrentToolTip(root.panel);
if (tooltip != "")
{
var mouse = Mouse.current;
if (mouse is null) return;
var mousePos = mouse.position.ReadValue();
mousePos.y = Screen.height - mousePos.y;
var pos =RuntimePanelUtils.ScreenToPanel(label.panel,mousePos);
pos.x += 24;
if (pos.x + label.layout.width > label.panel.visualTree.layout.width)
{
//pos.x = label.panel.visualTree.layout.width - label.layout.width - label.layout.width;
pos.x-=label.layout.width+48;
}
label.visible = true;
label.text = tooltip;
label.transform.position = pos;
}
else
{
label.visible = false;
}
}
private string CurrentToolTip(IPanel panel)
{
// https://docs.unity3d.com/2022.2/Documentation/Manual/UIE-faq-event-and-input-system.html
if (!EventSystem.current.IsPointerOverGameObject()) return "";
var screenPosition = Input.mousePosition;
screenPosition.y = Screen.height - screenPosition.y;
VisualElement ve = panel.Pick(RuntimePanelUtils.ScreenToPanel(panel, screenPosition));
return ve == null ? "" : ve.tooltip;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 98955702b98a11c4e8563958148f5719
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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,

View 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);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8712287847fe6ba46a47620902775417
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c9e5c30fbdd2e9e448dbec24b1e72f3b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 43f1856918b6ffc4c9c35caba4007208
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b2e651f968bbf7f4eac0936bc040061e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,98 @@
using System;
using System.Collections;
using System.Collections.Generic;
using BITKit.Mod;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UIElements;
using static BITKit.Mod.DotNetSdkRoslynService;
namespace BITKit.UX
{
public class UXDotNetSdkRoslynService : MonoBehaviour
{
[UXBindPath("install-roslyn-button")]
private Button _installButton;
[UXBindPath("uninstall-roslyn-button")]
private Button _uninstallButton;
[UXBindPath("install-roslyn-fill")]
private VisualElement _installFill;
private void OnEnable()
{
OnInstall+=_OnInstall;
OnInstalled+=_OnInstalled;
OnUnInstalled+=_OnUnInstalled;
OnDownloadProgress+=_OnDownloadProgress;
OnInstallFailed += _OnInstallFailed;
}
private void OnDisable()
{
OnInstall-=_OnInstall;
OnInstalled-=_OnInstalled;
OnUnInstalled-=_OnUnInstalled;
OnDownloadProgress-=_OnDownloadProgress;
OnInstallFailed -= _OnInstallFailed;
}
private void Start()
{
UXUtils.Inject(this);
if (Installed)
{
_OnInstalled();
}
else
{
_OnUnInstalled();
}
_installButton.clicked +=()=>Install().Forget();
_uninstallButton.clicked +=()=>
{
_uninstallButton.SetEnabled(false);
UnInstall().Forget();
};
}
private void _OnInstall()
{
_installButton.text = "准备中...";
_installButton.SetEnabled(false);
_uninstallButton.SetEnabled(false);
}
private void _OnUnInstalled()
{
_installButton.text = "安装";
_uninstallButton.SetEnabled(false);
_installButton.SetEnabled(true);
_installFill.style.width = new StyleLength(Length.Percent(0));
}
private void _OnInstalled()
{
_installButton.text = "重新安装";
_installButton.SetEnabled(true);
_uninstallButton.SetEnabled(true);
_installFill.style.width = new StyleLength(Length.Percent(0));
}
private void _OnDownloadProgress(float progress)
{
if (progress >= 1)
{
_installButton.text = "安装中...";
}
else
{
_installButton.text = $"下载中...{progress:P}";
}
_installFill.style.width = new StyleLength(Length.Percent((int)(progress * 100)));
}
private void _OnInstallFailed(Exception e)
{
_installButton.tooltip = e.Message;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8c8c71e08d0a2544aae8e62cd2b86af2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,107 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.Pool;
using UnityEngine.UIElements;
namespace BITKit.UX
{
[Serializable]
public sealed class UXAllow : ICondition
{
public bool OnCheck() => UXAllowInput.Allow.Allow;
}
public class UXAllowInput : MonoBehaviour
{
[RuntimeInitializeOnLoadMethod]
private static void Reload()
{
Allow.Clear();
}
internal static readonly ValidHandle Allow=new();
[SerializeField] private UIDocument document;
private IPanel _panel;
[SerializeField, ReadOnly] private bool isAllow;
[SerializeField, ReadOnly] private bool globalAllow;
[SerializeField, ReadOnly] private string blockBy;
private bool isLocked;
private void Start()
{
_panel = document.rootVisualElement.panel;
}
private void OnEnable()
{
Allow.AddElement(this);
}
private void OnDisable()
{
Allow.RemoveElement(this);
}
private void Update()
{
var mousePos = Mouse.current.position.ReadValue();
mousePos.y = Screen.height - mousePos.y;
var ve =_panel.Pick(RuntimePanelUtils.ScreenToPanel(_panel,mousePos ));
if (ve is not null)
{
var list = ListPool<string>.Get();
var v = ve;
{
while (true)
{
list.Add(v.name);
if (v.parent is not null)
{
v = v.parent;
continue;
}
break;
}
}
blockBy = string.Join("\\", list);
list.Clear();
ListPool<string>.Release(list);
}
else
{
blockBy = "unblocked";
}
switch (ve,Mouse.current.press.isPressed)
{
case (null,false) when isLocked is false:
Allow.AddElement(this);
Allow.RemoveDisableElements(this);
break;
case (not null,false):
Allow.RemoveElement(this);
Allow.AddDisableElements(this);
break;
case (not null,true):
isLocked = true;
break;
case (null,false):
isLocked = false;
break;
}
isAllow = ve is null;
globalAllow = Allow.Allow;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 897637fa096036c4d9bf2b210c0ba558
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 10868c050b42e7d4f8dcd17a320df58d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,231 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Threading;
using AnotherFileBrowser.Windows;
using BITKit.Mod;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UIElements;
namespace BITKit.UX
{
public class UXModService : MonoBehaviour
{
[SerializeField] private UIDocument document;
[SerializeField] private VisualTreeAsset modTemplate;
[UXBindPath("open-mod-button")]
private Button _openModButton;
[UXBindPath("mods-container")]
private VisualElement _modsContainer;
[UXBindPath("return-button")]
private Button _returnButton;
[UXBindPath("mod-name-label")]
private Label _modNameLabel;
[UXBindPath("mod-description-label")]
private Label _modDescriptionLabel;
[UXBindPath("reload-mod-button",true)]
private Button reloadModButton;
private readonly ConcurrentDictionary<string,UXContainer> _modContainers=new();
private void OnEnable()
{
ModService.OnModInstalled+=OnModInstalled;
ModService.OnModUnInstalled+=OnModUnInstalled;
ModService.OnModLoaded+=OnModLoaded;
ModService.OnModUnLoaded+=OnModUnLoaded;
ModService.OnLocked+=OnLocked;
}
private void OnDisable()
{
ModService.OnModInstalled-=OnModInstalled;
ModService.OnModUnInstalled-=OnModUnInstalled;
ModService.OnModLoaded-=OnModLoaded;
ModService.OnModUnLoaded-=OnModUnLoaded;
ModService.OnLocked-=OnLocked;
}
private void OnLocked(bool obj)
{
document.rootVisualElement.SetEnabled(!obj);
}
private void Start()
{
UXUtils.Inject(this);
if (_openModButton is not null)
{
_openModButton.clicked += OpenMod;
}
if (_returnButton is not null)
{
_returnButton.clicked += UXService.Return;
}
_modsContainer.Clear();
foreach (var x in ModService.Mods)
{
OnModInstalled(x);
}
if (reloadModButton is not null)
reloadModButton.clicked += async () =>
{
reloadModButton.SetEnabled(false);
await ModService.Reload();
if (destroyCancellationToken.IsCancellationRequested)
return;
await UniTask.SwitchToMainThread();
if (destroyCancellationToken.IsCancellationRequested)
return;
reloadModButton.SetEnabled(true);
};
}
private async void OnModUnInstalled(IMod obj)
{
await UniTask.SwitchToMainThread();
if (destroyCancellationToken.IsCancellationRequested) return;
_modContainers.TryRemove(obj.Name, out var container);
container.visualElement.RemoveFromHierarchy();
}
private async void OnModInstalled(IMod obj)
{
await UniTask.SwitchToMainThread();
if (destroyCancellationToken.IsCancellationRequested) return;
var container = _modContainers.GetOrAdd(obj.Name,_=> Create(obj));
container.visualElement.RegisterCallback<MouseDownEvent>(x =>
{
if (x.button != 1) return;
ContextMenuBuilder.Create().BuildAction("卸载", () =>
{
ModService.UnLoad(obj);
ModService.UnInstall(obj);
}).Build();
});
}
private async void OnModUnLoaded(IMod obj)
{
await UniTask.SwitchToMainThread();
if (destroyCancellationToken.IsCancellationRequested) return;
//var container = _modContainers.GetOrAdd(obj.Name,_=> Create(obj));
if(_modContainers.TryGetValue(obj.Name,out var container))
{
container.toggle.SetValueWithoutNotify(false);
}
}
private async void OnModLoaded(IMod obj)
{
await UniTask.SwitchToMainThread();
if (destroyCancellationToken.IsCancellationRequested) return;
var container = _modContainers.GetOrAdd(obj.Name,_=> Create(obj));
container.toggle.SetValueWithoutNotify(true);
}
private static void OpenMod()
{
BIT4Log.Log("正在打开选择文件对话框");
#if UNITY_EDITOR
new Thread(OpenModInternal).Start();
#else
OpenModInternal();
#endif
return;
void OpenModInternal()
{
BIT4Log.Log<UXModService>("已进入文件对话框线程");
new FileBrowser().OpenFileBrowser(new BrowserProperties()
{
//filterIndex = 0,
//filter = "C Sharp files (*.cs)|*.cs |Dll files (*.dll)|*.dll",
}, Filepath);
return;
async void Filepath(string path)
{
BIT4Log.Log<UXModService>("已选择文件:"+path);
await BITApp.SwitchToMainThread();
try
{
var file = new FileInfo(path);
switch (file.Extension)
{
case ".cs":
var code = await File.ReadAllTextAsync(path);
var assembly = BITSharp.Compile(code);
await ModService.Load(assembly,Path.GetDirectoryName(path));
break;
case ".dll":
var bytes = await File.ReadAllBytesAsync(path);
await ModService.Load(Assembly.Load(bytes),Path.GetDirectoryName(path));
break;
case ".json" when file.Name is ModPackage.DefaultFileName:
await ModService.LoadFromPackage(path);
break;
default:
Alert.Print(new AlertMessage()
{
title = "加载失败",
message = "不支持的文件类型"
});
break;
}
}
catch (Exception e)
{
Alert.Print(new AlertMessage()
{
title = "加载失败",
message = e.Message
});
BIT4Log.LogException(e);
}
}
}
}
private UXContainer Create(IMod mod)
{
var container = new UXContainer(_modsContainer.Create(modTemplate))
{
titleLabel =
{
text = mod.Name
},
descriptionLabel =
{
text = mod.Description
}
};
container.toggle.RegisterValueChangedCallback(evt =>
{
if (evt.newValue)
{
ModService.Load(mod);
}
else
{
ModService.UnLoad(mod);
}
});
container.visualElement.tooltip = mod.Name+"\n"+mod.Description;
container.button.clicked += () =>
{
_modNameLabel.text = mod.Name;
_modDescriptionLabel.text = mod.Description;
};
return container;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b3dfdbe1592c92b478ac90ee712f86ad
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: af0fbd41aa0222b488487e2101a95dd8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,33 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
namespace BITKit.UX
{
public class UXPhotoMode : MonoBehaviour
{
[SerializeField] private UIDocument document;
[UXBindPath("photo-mode-toggle")]
private Toggle _photoModeToggle;
private VisualElement[] _visualElements=Array.Empty<VisualElement>();
private void Start()
{
UXUtils.Inject(this);
_photoModeToggle.RegisterValueChangedCallback(OnValueChanged);
OnValueChanged(ChangeEvent<bool>.GetPooled(false,_photoModeToggle.value));
_visualElements = document.rootVisualElement.Query<VisualElement>(className:"photo-mode").ToList().ToArray();
}
private void OnValueChanged(ChangeEvent<bool> evt)
{
foreach (var x in _visualElements)
{
x.style.visibility=!evt.newValue?Visibility.Visible:Visibility.Hidden;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 35e8dc26ea80fbf41b5aae1689220a4b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 55f386f46df0fab428ab5a34187e000c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,83 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using BITKit;
using BITKit.UX;
using UnityEngine;
using UnityEngine.UIElements;
namespace BITKit.UX
{
[Serializable]
public class UXPopupSelector:IUXPopup
{
[SerializeReference,SubclassSelector] private IReference popupPath;
public void Popup(string content, float duration = 3)
{
UXPopup.Dictionary[popupPath.Value].Popup(content,duration);
}
}
public sealed class UXPopup : MonoBehaviour,IUXPopup
{
internal static readonly Dictionary<string,UXPopup> Dictionary=new();
[SerializeField] private UIDocument document;
[SerializeReference, SubclassSelector] private IReference containerPath;
[SerializeReference, SubclassSelector] private IReference popupPath;
[SerializeField] private VisualTreeAsset popupTemplate;
private VisualElement container;
private readonly List<ViewModel> instances = new();
private class ViewModel
{
public VisualElement visualElement;
public float exitTime;
public bool disposed;
}
private void Start()
{
container = document.rootVisualElement.Q<VisualElement>(containerPath.Value);
Dictionary.Add(popupPath.Value,this);
destroyCancellationToken.Register(() => Dictionary.Remove(popupPath.Value));
container.Clear();
}
private void Update()
{
foreach (var x in instances.Where(x=>Time.time > x.exitTime))
{
x.visualElement.RemoveFromHierarchy();
x.visualElement.SetOpacity(
Mathf.MoveTowards(x.visualElement.GetOpacity(),0,Time.deltaTime)
);
x.disposed = x.visualElement.GetOpacity() is 0;
}
instances.RemoveAll(x =>x.disposed);
container.SetActive(container.childCount > 0);
}
public void Popup(string content, float duration = 3)
{
var visualElement =new UXContainer(container.Create<VisualElement>(popupTemplate.CloneTree))
{
contextLabel =
{
text = content
}
};
instances.Add(new ViewModel()
{
visualElement = visualElement,
exitTime = Time.time + duration
});
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 98ea2f87d78be1f4a8369c9400bf4814
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,5 +1,6 @@
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
using BITKit;
using BITKit.SubSystems.Quest;
@@ -7,84 +8,85 @@ using UnityEngine.UIElements;
using System.Threading.Tasks;
namespace BITKit.UX
{
public class UXQuest : UXElement<VisualElement>
public class UXQuest : MonoBehaviour
{
[Header(Constant.Header.Settings)]
public bool clearAfterCompleted;
[Header(Constant.Header.Reference)]
[SerializeReference, SubclassSelector] public References completeClassName;
[SerializeReference, SubclassSelector] public IReference completeClassName;
[Header(Constant.Header.Prefabs)]
public VisualTreeAsset visualTreeAsset;
[Header(Constant.Header.InternalVariables)]
public Dictionary<string, UXContainer> dictionary = new();
public override void OnStart()
private readonly Dictionary<string, UXContainer> dictionary = new();
[UXBindPath("quest-container")]
private VisualElement _container;
private void Start()
{
base.OnStart();
UXUtils.Inject(this);
QuestSystem.OnQuestCreated += OnQuestCreated;
QuestSystem.OnQuestCompleted += OnQuestCompleted;
QuestSystem.OnQuestCanceled += OnQuestCancened;
QuestSystem.OnQuestCanceled += OnQuestCanceled;
visualElement.Clear();
_container.Clear();
}
void OnDestroy()
private void OnDestroy()
{
QuestSystem.OnQuestCreated -= OnQuestCreated;
QuestSystem.OnQuestCompleted -= OnQuestCompleted;
QuestSystem.OnQuestCanceled -= OnQuestCancened;
QuestSystem.OnQuestCanceled -= OnQuestCanceled;
}
void OnQuestCreated(QuestSystem.Info quest)
private void OnQuestCreated(QuestSystem.Info quest)
{
BIT4Log.Log<UXQuest>($"已创建任务:{quest.name}");
BIT4Log.Log<UXQuest>($"已创建任务:{quest.Name}");
if (dictionary.TryGetValue(quest.name, out var container))
if (dictionary.TryGetValue(quest.Name, out var container))
{
container.visualElement.RemoveFromClassList(completeClassName);
container.visualElement.RemoveFromClassList(completeClassName.Value);
}
else
{
container = new(visualTreeAsset.CloneTree());
dictionary.Add(quest.name, container);
dictionary.Add(quest.Name, container);
}
container.titleLabel.text = quest.name;
container.descriptionLabel.text = quest.description;
container.titleLabel.text = quest.Name;
container.descriptionLabel.text = quest.Description;
visualElement.Add(container);
_container.Add(container);
}
void OnQuestCancened(QuestSystem.Info quest)
private void OnQuestCanceled(QuestSystem.Info quest)
{
BIT4Log.Log<UXQuest>($"已取消任务:{quest.name}");
if (dictionary.TryGetValue(quest.name, out var container))
BIT4Log.Log<UXQuest>($"已取消任务:{quest.Name}");
if (!dictionary.TryGetValue(quest.Name, out var container)) return;
_container.Remove(container);
dictionary.Remove(quest.Name);
}
private async void OnQuestCompleted(QuestSystem.Info quest)
{
BIT4Log.Log<UXQuest>($"已完成任务:{quest.Name}");
if (!dictionary.TryGetValue(quest.Name, out var container)) return;
container.visualElement.AddToClassList(completeClassName.Value);
container.titleLabel.text = $"已完成:{quest.Name}";
container.visualElement.tabIndex = 256;
if (!clearAfterCompleted) return;
dictionary.Remove(quest.Name);
try
{
visualElement.Remove(container);
dictionary.Remove(quest.name);
await Task.Delay(8096, destroyCancellationToken);
}
}
async void OnQuestCompleted(QuestSystem.Info quest)
{
BIT4Log.Log<UXQuest>($"已完成任务:{quest.name}");
if (dictionary.TryGetValue(quest.name, out var container))
catch (System.OperationCanceledException) { }
catch (System.Exception)
{
container.visualElement.AddToClassList(completeClassName);
container.titleLabel.text = $"已完成:{quest.name}";
container.visualElement.tabIndex = 256;
if (clearAfterCompleted)
{
dictionary.Remove(quest.name);
try
{
await Task.Delay(8096, cancellationToken);
}
catch (System.OperationCanceledException) { }
catch (System.Exception)
{
visualElement.Remove(container);
throw;
}
}
_container.Remove(container);
throw;
}
}
}

View File

@@ -13,6 +13,17 @@ namespace BITKit.UX
{
public class UIToolKitPanel : MonoBehaviour,IUXPanel
{
[RuntimeInitializeOnLoadMethod]
private static void Reload()
{
inputActionGroup = new InputActionGroup
{
allowGlobalActivation = false,
Source = nameof(UIToolKitPanel)
};
inputActionGroup.allowInput.AddElement(0);
}
public UIToolKitPanel()
{
Index = GetType().FullName;
@@ -29,13 +40,14 @@ namespace BITKit.UX
[SerializeField] private Optional<float> entryDuration;
[SerializeField] private Optional<float> exitDuration;
protected readonly InputActionGroup inputActionGroup = new()
protected static InputActionGroup inputActionGroup = new()
{
allowGlobalActivation = false
allowGlobalActivation = false,
Source = nameof(UIToolKitPanel)
};
public bool IsAnimate => isAnimate;
public bool IsValid => cancellationToken.IsCancellationRequested is false;
public string Index { get; private set; }
public string Index { get; private set; }
public bool AllowCursor => allowCursor;
public bool AllowInput => allowInput;
protected CancellationToken cancellationToken { get; private set; }
@@ -60,6 +72,7 @@ namespace BITKit.UX
}
protected virtual void Start()
{
BITKit.UX.UXUtils.Inject(this);
if(IsValid && autoEntry)
UXService.Entry(this);
}
@@ -71,13 +84,15 @@ namespace BITKit.UX
protected virtual void OnEnable()=>UXService.Register(this);
protected virtual void OnDisable()=>UXService.UnRegister(this);
protected virtual void OnPanelEntry(){}
protected virtual void OnPanelExit(){}
void IEntryElement.Entry()
{
try
{
CurrentOpacity = 0;
TargetOpacity = 1;
OnEntryOrExit(true);
document.rootVisualElement.SetActive(true);
OnEntry?.Invoke();
}
@@ -97,15 +112,16 @@ namespace BITKit.UX
}
void IEntryElement.Entered()
{
inputActionGroup.allowInput.AddElement(this);
//inputActionGroup.allowInput.AddElement(this);
OnPanelEntry();
}
void IEntryElement.Exit()
{
if (IsValid is false) return;
TargetOpacity = 0;
OnEntryOrExit(false);
inputActionGroup.allowInput.RemoveElement(this);
//inputActionGroup.allowInput.RemoveElement(this);
OnPanelExit();
}
async UniTask IEntryElement.ExitAsync()
{
@@ -124,10 +140,6 @@ namespace BITKit.UX
public event Action OnEntry;
public event Action OnExit;
protected virtual void OnEntryOrExit(bool isEntry)
{
}
public virtual void OnUpdate(float deltaTime)
{
var duration = 1f;

View File

@@ -90,8 +90,6 @@ namespace BITKit.UX
private void Awake()
{
DI.Register<IUXService>(this);
}
private void Start()
{

View File

@@ -25,6 +25,8 @@ namespace BITKit.UX.Settings
private void Start()
{
UXUtils.Inject(this);
_resolutionDropdown.choices = Screen.resolutions.Select(x => x.ToString()).ToList();
_resolutionDropdown.SetValueWithoutNotify(Screen.currentResolution.ToString());
@@ -54,7 +56,14 @@ namespace BITKit.UX.Settings
Data.RemoveListender<string>(Constant.Environment.mat_setvideomode, OnResolutionChanged);
Data.RemoveListender<bool>(Constant.Environment.fullscreen,OnFullScreenChanged);
Data.RemoveListender<float>(Constant.Environment.sensitivity,OnSensitivityChanged);
PlayerPrefs.Save();
});
if(PlayerPrefs.HasKey(Constant.Environment.fullscreen))
Data.Set(Constant.Environment.fullscreen, PlayerPrefs.GetInt(Constant.Environment.fullscreen) == 1);
if(PlayerPrefs.HasKey(Constant.Environment.sensitivity))
Data.Set(Constant.Environment.sensitivity, PlayerPrefs.GetFloat(Constant.Environment.sensitivity));
}
private async void OnSensitivityChanged(float obj)
@@ -63,6 +72,8 @@ namespace BITKit.UX.Settings
PlayerConfig.Singleton.Sensitivity = obj;
_sensitivitySlider.SetValueWithoutNotify(obj);
PlayerPrefs.SetFloat(Constant.Environment.sensitivity,obj);
}
private async void OnFullScreenChanged(bool obj)
@@ -86,6 +97,7 @@ namespace BITKit.UX.Settings
#else
Screen.fullScreen = obj;
#endif
PlayerPrefs.SetInt(Constant.Environment.fullscreen, obj ? 1 : 0);
}
private async void OnResolutionChanged(string newResolution)
{
@@ -95,6 +107,7 @@ namespace BITKit.UX.Settings
var resolution = Screen.resolutions.Single(x => x.ToString() == newResolution);
Screen.SetResolution(resolution.width,resolution.height,Data.Get<bool>(Constant.Environment.fullscreen));
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f00bf577e456ef944b4226a77a2d3133
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View 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();
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 13a0fd39c05238f4e8537e85e254f541
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -11,44 +11,66 @@ namespace BITKit.UX
public class UXBindPathAttribute : Attribute
{
public string Path;
public bool CanBeNull;
public UXBindPathAttribute(){}
public UXBindPathAttribute(string path)
{
Path = path;
}
public UXBindPathAttribute(string path,bool canBeNull)
{
Path = path;
CanBeNull = canBeNull;
}
}
public class UXUtils
{
public static void Inject(object self)
{
UIDocument document;
var field = self.GetType().GetField("document",
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (field is null)
switch (field)
{
BIT4Log.Warning<UXUtils>($"{self.GetType().Name} not find {nameof(UIDocument)}");
return;
}
if (field.GetValue(self) is not UIDocument document)
{
if (self is MonoBehaviour monoBehaviour && (document = monoBehaviour.GetComponentInParent<UIDocument>()))
{
field.SetValue(self,document);
}
else
{
BIT4Log.Warning<UXUtils>($"document 未赋值");
case null when self is MonoBehaviour monoBehaviour && monoBehaviour.TryGetComponent<UIDocument>(out document):
break;
case not null when field.GetValue(self) is UIDocument _document:
document = _document;
break;
default:
BIT4Log.Warning<UXUtils>($"document 未赋值或未找到");
return;
}
}
foreach (var fieldInfo in self.GetType()
.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(x=>x.GetCustomAttribute<UXBindPathAttribute>() is not null)
)
)
{
var bindPathAtt = fieldInfo.GetCustomAttribute<UXBindPathAttribute>();
var 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 (field.GetValue(self) is not UIDocument _document)
// {
// if (self is MonoBehaviour monoBehaviour && (document = monoBehaviour.GetComponentInParent<UIDocument>()))
// {
// field.SetValue(self,document);
// }
// else
// {
// BIT4Log.Warning<UXUtils>($"document 未赋值");
// return;
// }
// }
}
}
}

View 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;
}
}
}

View File

@@ -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);
}
}
}