1
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
8
Src/Unity/Scripts/UX/ApplicationFile.meta
Normal file
8
Src/Unity/Scripts/UX/ApplicationFile.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 52bef53d9f175424da94066fc9eb38d5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
56
Src/Unity/Scripts/UX/ApplicationFile/UXApplicationFile.cs
Normal file
56
Src/Unity/Scripts/UX/ApplicationFile/UXApplicationFile.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2894e80bc1f4e5f469a6bbfe4e7f3b46
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -16,7 +16,8 @@
|
||||
"GUID:9400d40641bab5b4a9702f65bf5c6eb5",
|
||||
"GUID:7d3ace4c6aad3684abe11aa38b6cdf99",
|
||||
"GUID:517785bb4600a5140b47eac5fa49b8fc",
|
||||
"GUID:838d3286f0973344ab6e99d3951012f7"
|
||||
"GUID:838d3286f0973344ab6e99d3951012f7",
|
||||
"GUID:a11ff146d38b27a44af87b4b4d9c4ecb"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
|
@@ -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();
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
@@ -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);
|
||||
|
@@ -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)) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
62
Src/Unity/Scripts/UX/Core/UXToolTips.cs
Normal file
62
Src/Unity/Scripts/UX/Core/UXToolTips.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
11
Src/Unity/Scripts/UX/Core/UXToolTips.cs.meta
Normal file
11
Src/Unity/Scripts/UX/Core/UXToolTips.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 98955702b98a11c4e8563958148f5719
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -5,11 +5,14 @@
|
||||
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
|
||||
"GUID:6ef4ed8ff60a7aa4bb60a8030e6f4008",
|
||||
"GUID:d525ad6bd40672747bde77962f1c401e",
|
||||
"GUID:49b49c76ee64f6b41bf28ef951cb0e50"
|
||||
"GUID:49b49c76ee64f6b41bf28ef951cb0e50",
|
||||
"GUID:90b448749ba9be04ebf2eb5953b53caf",
|
||||
"GUID:e0cd26848372d4e5c891c569017e11f1",
|
||||
"GUID:d8b63aba1907145bea998dd612889d6b"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"allowUnsafeCode": true,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
|
136
Src/Unity/Scripts/UX/DataBind/UXDataBindingHandler.cs
Normal file
136
Src/Unity/Scripts/UX/DataBind/UXDataBindingHandler.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using BITKit.UX;
|
||||
using Newtonsoft.Json;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using Cursor = UnityEngine.UIElements.Cursor;
|
||||
|
||||
namespace BITKit.IData
|
||||
{
|
||||
public interface IUXDataBinder:IDisposable
|
||||
{
|
||||
Type TargetType { get; }
|
||||
|
||||
void OnTick(float deltaTime);
|
||||
|
||||
VisualElement CreateUI(object target,FieldInfo fieldInfo);
|
||||
VisualElement VisualElement { get; }
|
||||
}
|
||||
public abstract class UXDataBinder<TDataType> : IUXDataBinder
|
||||
{
|
||||
public Type TargetType => typeof(TDataType);
|
||||
public VisualElement VisualElement { get; private set; }
|
||||
protected object target{ get; private set; }
|
||||
protected TDataType currentValue { get; set; }
|
||||
protected FieldInfo fieldInfo{ get; private set; }
|
||||
protected string Name { get; private set; }
|
||||
|
||||
public virtual void OnTick(float deltaTime)
|
||||
{
|
||||
var newValue = (TDataType)fieldInfo.GetValue(target);
|
||||
if(Equals(newValue,currentValue))return;
|
||||
OnValueChanged(currentValue,newValue);
|
||||
}
|
||||
protected virtual void OnValueChanged(TDataType oldValue,TDataType newValue)
|
||||
{
|
||||
currentValue = newValue;
|
||||
fieldInfo.SetValue(target,currentValue);
|
||||
|
||||
if (VisualElement is INotifyValueChanged<TDataType> notifyValueChanged)
|
||||
notifyValueChanged.SetValueWithoutNotify(newValue);
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
target = null;
|
||||
fieldInfo = null;
|
||||
VisualElement.RemoveFromHierarchy();
|
||||
VisualElement = null;
|
||||
}
|
||||
|
||||
public VisualElement CreateUI(object target, FieldInfo fieldInfo)
|
||||
{
|
||||
this.target = target;
|
||||
this.fieldInfo = fieldInfo;
|
||||
currentValue = (TDataType)fieldInfo.GetValue(target);
|
||||
|
||||
VisualElement = OnCreateUI();
|
||||
|
||||
var attribute = fieldInfo.GetCustomAttribute<ExportAttribute>();
|
||||
|
||||
if(attribute != null)
|
||||
{
|
||||
Name = attribute.Name;
|
||||
VisualElement.GetType().GetProperty("label",ReflectionHelper.Flags)!.SetValue(VisualElement, Name);
|
||||
}
|
||||
if(VisualElement is INotifyValueChanged<TDataType> notifyValueChanged)
|
||||
{
|
||||
notifyValueChanged.SetValueWithoutNotify(currentValue);
|
||||
notifyValueChanged.RegisterValueChangedCallback(x =>
|
||||
{
|
||||
fieldInfo.SetValue(target,x.newValue);
|
||||
currentValue = x.newValue;
|
||||
});
|
||||
}
|
||||
return VisualElement;
|
||||
}
|
||||
|
||||
|
||||
protected abstract VisualElement OnCreateUI();
|
||||
}
|
||||
|
||||
public sealed class UXIntBinder:UXDataBinder<int>
|
||||
{
|
||||
protected override VisualElement OnCreateUI() => new IntegerField();
|
||||
}
|
||||
public sealed class UXFloatBinder:UXDataBinder<float>
|
||||
{
|
||||
protected override VisualElement OnCreateUI() => new FloatField();
|
||||
}
|
||||
public sealed class UXVector3Binder:UXDataBinder<Vector3>
|
||||
{
|
||||
protected override VisualElement OnCreateUI() => new Vector3Field();
|
||||
}
|
||||
public sealed class UXVector2Binder:UXDataBinder<Vector2>
|
||||
{
|
||||
protected override VisualElement OnCreateUI() => new Vector2Field();
|
||||
}
|
||||
public sealed class UXStringBinder:UXDataBinder<string>
|
||||
{
|
||||
protected override VisualElement OnCreateUI() => new TextField()
|
||||
{
|
||||
isDelayed = true,
|
||||
multiline = true
|
||||
};
|
||||
}
|
||||
public sealed class UXFloat3Binder:UXDataBinder<float3>
|
||||
{
|
||||
protected override VisualElement OnCreateUI()
|
||||
{
|
||||
var field = new Vector3Field
|
||||
{
|
||||
label = Name
|
||||
};
|
||||
field.SetValueWithoutNotify(currentValue);
|
||||
field.RegisterCallback<ChangeEvent<Vector3>>(x =>
|
||||
{
|
||||
fieldInfo.SetValue(target,(float3)x.newValue);
|
||||
});
|
||||
return field;
|
||||
}
|
||||
protected override void OnValueChanged(float3 oldValue, float3 newValue)
|
||||
{
|
||||
currentValue = newValue;
|
||||
VisualElement.As<INotifyValueChanged<Vector3>>().SetValueWithoutNotify(newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
11
Src/Unity/Scripts/UX/DataBind/UXDataBindingHandler.cs.meta
Normal file
11
Src/Unity/Scripts/UX/DataBind/UXDataBindingHandler.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8712287847fe6ba46a47620902775417
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
93
Src/Unity/Scripts/UX/DataBind/UXDataBindingService.cs
Normal file
93
Src/Unity/Scripts/UX/DataBind/UXDataBindingService.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using BITKit.UX;
|
||||
using Newtonsoft.Json;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
|
||||
namespace BITKit.IData
|
||||
{
|
||||
public class UXDataBindingService : MonoBehaviour
|
||||
{
|
||||
[RuntimeInitializeOnLoadMethod]
|
||||
private static void Reload()
|
||||
{
|
||||
_Binders.Clear();
|
||||
_Instances.Clear();
|
||||
}
|
||||
private static readonly Dictionary<Type,IUXDataBinder> _Binders = new();
|
||||
private static readonly CacheList<IUXDataBinder> _Instances = new();
|
||||
|
||||
public static IUXDataBinder Create(object target,FieldInfo fieldInfo)
|
||||
{
|
||||
var value = fieldInfo.GetValue(target);
|
||||
var type = value?.GetType() ?? fieldInfo.FieldType;
|
||||
|
||||
// if (value is IEnumerable)
|
||||
// {
|
||||
// //type = typeof(IEnumerable<>).MakeGenericType(fieldInfo.FieldType.GetElementType());
|
||||
// //type = typeof(IEnumerable<>).MakeGenericType(type.GetGenericArguments());
|
||||
// }
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case Array:
|
||||
type = typeof(IEnumerable<>).MakeGenericType(fieldInfo.FieldType.GetElementType());
|
||||
break;
|
||||
case IList:
|
||||
type = typeof(IEnumerable<>).MakeGenericType(type.GetGenericArguments());
|
||||
break;
|
||||
}
|
||||
|
||||
if (_Binders.TryGetValue(type, out var binder) is false)
|
||||
throw new NotImplementedException($"未支持的类型:{type.Name}");
|
||||
|
||||
var instance = (IUXDataBinder)Activator.CreateInstance(binder.GetType());
|
||||
_Instances.Add(instance);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
[SerializeReference, SubclassSelector] private ITicker ticker;
|
||||
|
||||
private async void Start()
|
||||
{
|
||||
var binders = await ReflectionHelper.GetTypes<IUXDataBinder>();
|
||||
if(destroyCancellationToken.IsCancellationRequested)return;
|
||||
|
||||
foreach (var x in binders)
|
||||
{
|
||||
var instance = (IUXDataBinder)Activator.CreateInstance(x);
|
||||
if (_Binders.TryAdd(instance.TargetType, instance)) ;
|
||||
}
|
||||
|
||||
ticker.Add(OnTick);
|
||||
|
||||
destroyCancellationToken.Register(() =>
|
||||
{
|
||||
ticker.Remove(OnTick);
|
||||
});
|
||||
}
|
||||
private static void OnTick(float obj)
|
||||
{
|
||||
var array = _Instances.ValueArray;
|
||||
foreach (var x in array)
|
||||
{
|
||||
if (x.VisualElement.panel is null)
|
||||
{
|
||||
_Instances.Remove(x);
|
||||
continue;
|
||||
}
|
||||
x.OnTick(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Src/Unity/Scripts/UX/DataBind/UXDataBindingService.cs.meta
Normal file
11
Src/Unity/Scripts/UX/DataBind/UXDataBindingService.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c9e5c30fbdd2e9e448dbec24b1e72f3b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
193
Src/Unity/Scripts/UX/DataBind/UXIEnumerableBindingHandler.cs
Normal file
193
Src/Unity/Scripts/UX/DataBind/UXIEnumerableBindingHandler.cs
Normal file
@@ -0,0 +1,193 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace BITKit.IData
|
||||
{
|
||||
public abstract class UXIEnumerableBindingHandler<TDataType> : IUXDataBinder
|
||||
{
|
||||
|
||||
public Type TargetType =>typeof(IEnumerable<TDataType>);
|
||||
public VisualElement VisualElement { get;private set; }
|
||||
protected object target { get; private set; }
|
||||
protected FieldInfo fieldInfo{ get; private set; }
|
||||
protected VisualElement container { get; private set; }
|
||||
protected Button addButton;
|
||||
protected readonly List<TDataType> interfaceList=new();
|
||||
|
||||
public VisualElement CreateUI(object target, FieldInfo fieldInfo)
|
||||
{
|
||||
if(VisualElement is not null){throw new InvalidOperationException($"已经创建了Inspector<{VisualElement.GetType().Name}>");}
|
||||
var exportAttribute = fieldInfo.GetCustomAttribute<ExportAttribute>();
|
||||
|
||||
this.fieldInfo = fieldInfo;
|
||||
this.target = target;
|
||||
|
||||
// VisualElement = new Foldout();
|
||||
//
|
||||
// VisualElement.Create<Label>().text = exportAttribute switch
|
||||
// {
|
||||
// not null when string.IsNullOrEmpty(exportAttribute.Name) is false => exportAttribute.Name,
|
||||
// _=>fieldInfo.Name
|
||||
// };
|
||||
|
||||
VisualElement = new Foldout()
|
||||
{
|
||||
text = exportAttribute switch
|
||||
{
|
||||
not null when string.IsNullOrEmpty(exportAttribute.Name) is false => exportAttribute.Name,
|
||||
_=>fieldInfo.Name
|
||||
}
|
||||
};
|
||||
VisualElement.AddToClassList("--button");
|
||||
|
||||
container = VisualElement.Create<VisualElement>();
|
||||
addButton = VisualElement.Create<Button>();
|
||||
|
||||
addButton.text = "+";
|
||||
|
||||
addButton.clicked += Add;
|
||||
|
||||
Rebuild();
|
||||
|
||||
return VisualElement;
|
||||
}
|
||||
|
||||
|
||||
private void Add()
|
||||
{
|
||||
interfaceList.Add(default);
|
||||
|
||||
switch (fieldInfo.GetValue(target))
|
||||
{
|
||||
case List<TDataType> list:
|
||||
list.Add(default);
|
||||
break;
|
||||
case TDataType[] array:
|
||||
Array.Resize(ref array, array.Length + 1);
|
||||
fieldInfo.SetValue(target, array);
|
||||
break;
|
||||
}
|
||||
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
protected void Rebuild()
|
||||
{
|
||||
interfaceList.Clear();
|
||||
interfaceList.AddRange((IEnumerable<TDataType>)fieldInfo.GetValue(target));
|
||||
|
||||
container.Clear();
|
||||
|
||||
for (var index = 0; index < interfaceList.Count; index++)
|
||||
{
|
||||
var index1 = index;
|
||||
var entryContainer = container.Create<VisualElement>();
|
||||
|
||||
entryContainer.style.flexDirection = FlexDirection.Row;
|
||||
|
||||
var x = interfaceList[index];
|
||||
|
||||
var textField = entryContainer.Create<TextField>();
|
||||
|
||||
textField.isDelayed = true;
|
||||
|
||||
if(x is not null)
|
||||
textField.SetValueWithoutNotify(JsonConvert.SerializeObject(x));
|
||||
|
||||
textField.style.flexGrow = 1;
|
||||
textField.style.marginLeft = 0;
|
||||
|
||||
textField.RegisterValueChangedCallback(OnTextFieldOnValueChanged);
|
||||
|
||||
var removeButton = entryContainer.Create<Button>();
|
||||
//removeButton.text = $"-{index}";
|
||||
|
||||
//u know how to do
|
||||
removeButton.style.marginTop = 0;
|
||||
|
||||
removeButton.AddToClassList("icon-remove");
|
||||
|
||||
removeButton.clicked += OnRemoveButtonOnClicked;
|
||||
|
||||
container.Add(entryContainer);
|
||||
|
||||
continue;
|
||||
|
||||
void OnRemoveButtonOnClicked()
|
||||
{
|
||||
switch (fieldInfo.GetValue(target))
|
||||
{
|
||||
case List<TDataType> list:
|
||||
list.RemoveAt(index1);
|
||||
break;
|
||||
case TDataType[] array:
|
||||
interfaceList.RemoveAt(index1);
|
||||
fieldInfo.SetValue(target, interfaceList.ToArray());
|
||||
break;
|
||||
}
|
||||
Rebuild();
|
||||
}
|
||||
void OnTextFieldOnValueChanged(ChangeEvent<string> evt)
|
||||
{
|
||||
Deserialize(textField,index1,evt.newValue);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void OnTick(float deltaTime)
|
||||
{
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
// TODO 在此释放托管资源
|
||||
}
|
||||
protected void Deserialize(TextField textField,int index,string value)
|
||||
{
|
||||
try
|
||||
{
|
||||
var newValue = (TDataType)JsonConvert.DeserializeObject(value, typeof(TDataType));
|
||||
switch (fieldInfo.GetValue(target))
|
||||
{
|
||||
case List<TDataType> list:
|
||||
list[index] = newValue;
|
||||
break;
|
||||
case TDataType[] array:
|
||||
array[index] = newValue;
|
||||
fieldInfo.SetValue(target, array);
|
||||
break;
|
||||
}
|
||||
Rebuild();
|
||||
|
||||
textField.EnableInClassList("error",false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
textField.Query<TextElement>().First().tooltip = e.Message;
|
||||
textField.EnableInClassList("error",true);
|
||||
}
|
||||
}
|
||||
}
|
||||
public sealed class UXStringIEnumerableBindingHandler : UXIEnumerableBindingHandler<string>
|
||||
{
|
||||
}
|
||||
public sealed class UXFloatIEnumerableBindingHandler : UXIEnumerableBindingHandler<float>
|
||||
{
|
||||
}
|
||||
public sealed class UXIntIEnumerableBindingHandler : UXIEnumerableBindingHandler<int>
|
||||
{
|
||||
}
|
||||
public sealed class UXVector3IEnumerableBindingHandler : UXIEnumerableBindingHandler<Vector3>
|
||||
{
|
||||
}
|
||||
public sealed class UXFloat3IEnumerableBindingHandler : UXIEnumerableBindingHandler<float3>
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 43f1856918b6ffc4c9c35caba4007208
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Src/Unity/Scripts/UX/DotNetSdkRoslyn.meta
Normal file
8
Src/Unity/Scripts/UX/DotNetSdkRoslyn.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b2e651f968bbf7f4eac0936bc040061e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8c8c71e08d0a2544aae8e62cd2b86af2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
107
Src/Unity/Scripts/UX/Input/UXAllowInput.cs
Normal file
107
Src/Unity/Scripts/UX/Input/UXAllowInput.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
11
Src/Unity/Scripts/UX/Input/UXAllowInput.cs.meta
Normal file
11
Src/Unity/Scripts/UX/Input/UXAllowInput.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 897637fa096036c4d9bf2b210c0ba558
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Src/Unity/Scripts/UX/ModService.meta
Normal file
8
Src/Unity/Scripts/UX/ModService.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 10868c050b42e7d4f8dcd17a320df58d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
231
Src/Unity/Scripts/UX/ModService/UXModService.cs
Normal file
231
Src/Unity/Scripts/UX/ModService/UXModService.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
11
Src/Unity/Scripts/UX/ModService/UXModService.cs.meta
Normal file
11
Src/Unity/Scripts/UX/ModService/UXModService.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3dfdbe1592c92b478ac90ee712f86ad
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Src/Unity/Scripts/UX/PhotoMode.meta
Normal file
8
Src/Unity/Scripts/UX/PhotoMode.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af0fbd41aa0222b488487e2101a95dd8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
33
Src/Unity/Scripts/UX/PhotoMode/UXPhotoMode.cs
Normal file
33
Src/Unity/Scripts/UX/PhotoMode/UXPhotoMode.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
11
Src/Unity/Scripts/UX/PhotoMode/UXPhotoMode.cs.meta
Normal file
11
Src/Unity/Scripts/UX/PhotoMode/UXPhotoMode.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 35e8dc26ea80fbf41b5aae1689220a4b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Src/Unity/Scripts/UX/Popup.meta
Normal file
8
Src/Unity/Scripts/UX/Popup.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 55f386f46df0fab428ab5a34187e000c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
83
Src/Unity/Scripts/UX/Popup/UXPopup.cs
Normal file
83
Src/Unity/Scripts/UX/Popup/UXPopup.cs
Normal 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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
11
Src/Unity/Scripts/UX/Popup/UXPopup.cs.meta
Normal file
11
Src/Unity/Scripts/UX/Popup/UXPopup.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 98ea2f87d78be1f4a8369c9400bf4814
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -90,8 +90,6 @@ namespace BITKit.UX
|
||||
private void Awake()
|
||||
{
|
||||
DI.Register<IUXService>(this);
|
||||
|
||||
|
||||
}
|
||||
private void Start()
|
||||
{
|
||||
|
@@ -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));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
8
Src/Unity/Scripts/UX/UXBarService.meta
Normal file
8
Src/Unity/Scripts/UX/UXBarService.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f00bf577e456ef944b4226a77a2d3133
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
70
Src/Unity/Scripts/UX/UXBarService/UXBarService.cs
Normal file
70
Src/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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Src/Unity/Scripts/UX/UXBarService/UXBarService.cs.meta
Normal file
11
Src/Unity/Scripts/UX/UXBarService/UXBarService.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 13a0fd39c05238f4e8537e85e254f541
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
108
Src/Unity/Scripts/UX/Waiting/UXWaiting.cs
Normal file
108
Src/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