257 lines
6.6 KiB
C#
257 lines
6.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using BITKit.Mod;
|
|
using BITKit.StateMachine;
|
|
using Cysharp.Threading.Tasks;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Unity.Mathematics;
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
using UnityEngine.InputSystem;
|
|
using UnityEngine.UIElements;
|
|
using Object = UnityEngine.Object;
|
|
|
|
namespace BITKit.UX
|
|
{
|
|
/// <summary>
|
|
/// 适用于Unity的UX Service
|
|
/// </summary>
|
|
public class UXService : IUXService
|
|
{
|
|
private readonly IAfterTicker _ticker;
|
|
private readonly IServiceProvider _serviceProvider;
|
|
private readonly CancellationTokenSource _cancellationTokenSource;
|
|
public UXService(IAfterTicker ticker, IServiceProvider serviceProvider, CancellationTokenSource cancellationTokenSource)
|
|
{
|
|
_ticker = ticker;
|
|
_serviceProvider = serviceProvider;
|
|
_cancellationTokenSource = cancellationTokenSource;
|
|
_entryGroup.OnStateChanged += OnEntry;
|
|
|
|
_windowEntryGroup.OnStateChanged += OnWindowEntry;
|
|
_ticker.Add(OnTick);
|
|
}
|
|
private void OnWindowEntry(IUXPanel prev, IUXPanel next)
|
|
{
|
|
BITAppForUnity.AllowCursor.SetElements(_windowEntryGroup, next is { AllowCursor: true });
|
|
|
|
BITInputSystem.AllowInput.SetDisableElements(_windowEntryGroup, next is { AllowInput: false });
|
|
}
|
|
|
|
private readonly AsyncStateMachine<IUXPanel> _entryGroup = new();
|
|
private readonly AsyncStateMachine<IUXPanel> _windowEntryGroup = new();
|
|
/// <summary>
|
|
/// 内部注册面板队列
|
|
/// </summary>
|
|
private readonly Queue<IUXPanel> _registryQueue = new();
|
|
|
|
/// <summary>
|
|
/// 内部注销面板队列
|
|
/// </summary>
|
|
private readonly Queue<IUXPanel> _unRegistryQueue = new();
|
|
|
|
/// <summary>
|
|
/// 已注册面板字典
|
|
/// </summary>
|
|
private readonly Dictionary<string, IUXPanel> _panels = new();
|
|
|
|
/// <summary>
|
|
/// 等待启用的面板队列
|
|
/// </summary>
|
|
private readonly Stack<IUXPanel> _entryQueue = new();
|
|
private readonly List<string> _entryQueueByName = new();
|
|
|
|
/// <summary>
|
|
/// 返回面板缓冲区
|
|
/// </summary>
|
|
private readonly DoubleBuffer<IUXPanel> _returnBuffer = new();
|
|
|
|
/// <summary>
|
|
/// 已启用面板
|
|
/// </summary>
|
|
private IUXPanel _currentPanel;
|
|
|
|
/// <summary>
|
|
/// 历史面板
|
|
/// </summary>
|
|
private static readonly Stack<IUXPanel> History = new();
|
|
|
|
/// <summary>
|
|
/// 清空历史面板,通常用于关闭返回上一步
|
|
/// </summary>
|
|
public static void ClearHistory() => History.Clear();
|
|
|
|
public string SettingsPath { get; set; } = "ux_panel_settings";
|
|
public object Root { get; private set; }
|
|
public static VisualElement RootVisualElement { get; private set; }
|
|
public async UniTask InitializeAsync()
|
|
{
|
|
var gameObject = new GameObject("UXService");
|
|
Object.DontDestroyOnLoad(gameObject);
|
|
|
|
_cancellationTokenSource.Token.Register(() =>
|
|
{
|
|
Object.Destroy(gameObject);
|
|
});
|
|
|
|
var document = gameObject.AddComponent<UIDocument>();
|
|
try
|
|
{
|
|
if (Touchscreen.current is not null && SettingsPath == "ux_panel_settings")
|
|
{
|
|
SettingsPath = "ux_panel_settings_mobile";
|
|
}
|
|
|
|
var panelSettings =await ModService.LoadAsset<PanelSettings>(SettingsPath);
|
|
document.panelSettings = panelSettings;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
BIT4Log.Warning<UXService>("未找到ux_panel_settings");
|
|
throw;
|
|
}
|
|
|
|
Root = RootVisualElement= document.rootVisualElement;
|
|
|
|
if (Touchscreen.current is not null)
|
|
{
|
|
RootVisualElement.AddToClassList("mobile");
|
|
}
|
|
}
|
|
|
|
public void Register(IUXPanel panel) => _registryQueue.Enqueue(panel);
|
|
|
|
public void UnRegister(IUXPanel panel) => _unRegistryQueue.Enqueue(panel);
|
|
|
|
public void Entry<T>() where T : IUXPanel
|
|
{
|
|
var panel = _serviceProvider.GetRequiredService<T>();
|
|
Entry(panel);
|
|
//Entry(typeof(T).Name);
|
|
}
|
|
|
|
public void Entry(IUXPanel panel) => _entryQueue.Push(panel);
|
|
public void Entry(string panelName) => _entryQueueByName.TryAdd(panelName);
|
|
public IUXPanel CurrentPanel => _currentPanel;
|
|
public event Action<IUXPanel, IUXPanel> OnPanelChanged;
|
|
public bool TryPick(float2 position, out object obj)
|
|
{
|
|
obj = null;
|
|
if (!EventSystem.current.IsPointerOverGameObject())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
position.y = Screen.height - position.y;
|
|
|
|
var ve = RootVisualElement.panel.Pick(RuntimePanelUtils.ScreenToPanel(RootVisualElement.panel, position));
|
|
|
|
obj = ve;
|
|
|
|
return obj is not null;
|
|
}
|
|
|
|
public void Return()
|
|
{
|
|
if (_windowEntryGroup.CurrentState is not null)
|
|
{
|
|
_windowEntryGroup.DisposeState();
|
|
return;
|
|
}
|
|
if (History.TryPop(out var returnPanel))
|
|
{
|
|
_returnBuffer.Release(returnPanel);
|
|
}
|
|
}
|
|
|
|
|
|
private bool _initialized;
|
|
|
|
|
|
private void OnEntry(IUXPanel prev,IUXPanel next)
|
|
{
|
|
OnPanelChanged?.Invoke(_currentPanel,next);
|
|
_currentPanel = next;
|
|
}
|
|
private void OnTick(float delta)
|
|
{
|
|
try
|
|
{
|
|
|
|
while (_registryQueue.TryDequeue(out var result))
|
|
{
|
|
if (result is null) continue;
|
|
_entryGroup.Register(result);
|
|
_panels.Set(result.Index, result);
|
|
}
|
|
|
|
while (_unRegistryQueue.TryDequeue(out var result))
|
|
{
|
|
if (result is null) continue;
|
|
_entryGroup.UnRegister(result);
|
|
_panels.Remove(result.Index);
|
|
}
|
|
|
|
if (_returnBuffer.TryGetRelease(out var returnPanel))
|
|
{
|
|
_entryGroup.TransitionState(returnPanel);
|
|
BITAppForUnity.AllowCursor.SetElements(this, returnPanel.AllowCursor);
|
|
BITInputSystem.AllowInput.SetElements(this, returnPanel.AllowInput);
|
|
}
|
|
|
|
foreach (var panelName in _entryQueueByName)
|
|
{
|
|
if (!_panels.TryGetValue(panelName, out var panel))continue;
|
|
_entryQueue.Push(panel);
|
|
_entryQueueByName.TryRemove(panelName);
|
|
break;
|
|
}
|
|
if (_entryQueue.TryPop(out var nextPanel))
|
|
{
|
|
if (nextPanel.IsWindow)
|
|
{
|
|
_windowEntryGroup.TransitionState(nextPanel);
|
|
return;
|
|
}
|
|
_windowEntryGroup.DisposeState();
|
|
History.Push(_currentPanel);
|
|
_entryGroup.TransitionState(nextPanel);
|
|
BITAppForUnity.AllowCursor.SetElements(this, nextPanel.AllowCursor);
|
|
BITInputSystem.AllowInput.SetElements(this, nextPanel.AllowInput);
|
|
}
|
|
|
|
if (_entryGroup.CurrentState is {Enabled:true})
|
|
{
|
|
_entryGroup.CurrentState.OnTick(delta);
|
|
}
|
|
if (_windowEntryGroup.CurrentState is {Enabled:true})
|
|
{
|
|
_windowEntryGroup.CurrentState.OnTick(delta);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
BIT4Log.LogException(e);
|
|
}
|
|
}
|
|
|
|
public async void Dispose()
|
|
{
|
|
foreach (var panelsValue in _panels.Values)
|
|
{
|
|
if (panelsValue is IDisposable disposable)
|
|
{
|
|
disposable.Dispose();
|
|
}
|
|
}
|
|
_ticker.Remove(OnTick);
|
|
await UniTask.SwitchToMainThread();
|
|
_entryGroup.Dispose();
|
|
_windowEntryGroup.Dispose();
|
|
}
|
|
}
|
|
}
|
|
|