using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Threading; using BITKit.Mod; using BITKit.StateMachine; using Cysharp.Threading.Tasks; using UnityEngine; using UnityEngine.Serialization; using UnityEngine.InputSystem; using UnityEngine.UIElements; using Debug = UnityEngine.Debug; // ReSharper disable MemberCanBeProtected.Global // ReSharper disable ClassWithVirtualMembersNeverInherited.Global // ReSharper disable UnusedMember.Global // ReSharper disable MemberCanBePrivate.Global namespace BITKit.UX { public abstract class UIToolKitPanel :StateAsync, IUXPanel,IDisposable { public const string USSEntry = "transition_entry"; public const string USSEntryAsync = "transition_entry_async"; public const string USSEntered = "transition_entried"; public const string USSExit = "transition_exit"; public const string USSExitAsync = "transition_exit_async"; public const string USSExited = "transition_exited"; protected readonly IUXService UXService; protected abstract string DocumentPath { get; } public VisualElement RootVisualElement { get; set; } protected VisualTreeAsset VisualTreeAsset { get; private set; } private readonly ValidHandle _isBusy = new(); public readonly UniTaskCompletionSource WaitUtilInitialized = new(); protected UniTaskCompletionSource WaitUtilTransitionCompleted=new(); protected UIToolKitPanel(IUXService uxService) { UXService = uxService; uxService.Register(this); } public override async UniTask InitializeAsync() { await _isBusy; using var b = _isBusy.GetHandle(); if (RootVisualElement is null) { VisualTreeAsset = await ModService.LoadAsset(DocumentPath); try { RootVisualElement = UXService.Root.As().Create(VisualTreeAsset); } catch (Exception e) { Debug.LogWarning(GetType().Name+DocumentPath); throw; } RootVisualElement.name = DocumentPath; RootVisualElement.pickingMode = PickingMode.Ignore; RootVisualElement.style.position = Position.Absolute; RootVisualElement.style.left = 0; RootVisualElement.style.right = 0; RootVisualElement.style.top = 0; RootVisualElement.style.bottom = 0; foreach (var uss in InitialUssClasses) { RootVisualElement.AddToClassList(uss); } var invisible = RootVisualElement.Create(); invisible.name = "invisible_return_generate"; invisible.AddToClassList("background_generate"); invisible.style.position = Position.Absolute; invisible.pickingMode = PickingMode.Ignore; invisible.style.left = 0; invisible.style.right = 0; invisible.style.top = 0; invisible.style.bottom = 0; invisible.SendToBack(); if (CloseWhenClickOutside) { invisible.RegisterCallback(x => { OnReturn(); }); invisible.pickingMode = PickingMode.Position; } if (IsWindow) { invisible.style.backgroundColor = new Color(0, 0, 0, 0.9f); } UXUtils.Inject(this,RootVisualElement); RootVisualElement.SetActive(false); await OnInitiatedAsync.UniTaskFunc(); RootVisualElement.RegisterCallback(OnTransitionRun); RootVisualElement.RegisterCallback(OnTransitionStart); RootVisualElement.RegisterCallback(OnTransitionEnd); RootVisualElement.RegisterCallback(OnTransitionEnd); WaitUtilTransitionCompleted.TrySetResult(); WaitUtilInitialized.TrySetResult(); OnInitiated?.Invoke(); } } private void OnTransitionStart(TransitionStartEvent evt) { WaitUtilTransitionCompleted = new(); } private void OnTransitionRun(TransitionRunEvent evt) { //WaitUtilTransitionCompleted = new(); } protected readonly InputActionGroup InputActionGroup = new() { allowGlobalActivation = false, Source = nameof(UIToolKitPanel) }; public virtual bool CloseWhenClickOutside { get;} public virtual bool IsWindow { get; } public virtual string Index => GetType().Name; public virtual bool AllowReload { get; } public virtual bool AllowCursor { get; } public virtual bool AllowInput { get; } public object Root => RootVisualElement; public virtual string[] InitialUssClasses { get; } = Array.Empty(); public bool IsDisposed { get; private set; } public bool IsEntered { get; set; } protected virtual void OnReturn() { UXService.Return(); } protected virtual void OnPanelEntry(){} protected virtual void OnPanelExit(){} public override void OnStateEntry(IState old) { InputActionGroup.allowInput.AddElement(this); OnEntry?.Invoke(); } public override async UniTask OnStateEntryAsync(IState old) { await InitializeAsync(); //WaitUtilTransitionCompleted = new(); RootVisualElement.SetActive(true); //await UniTask.NextFrame(); if (IsWindow) { RootVisualElement.BringToFront(); } RootVisualElement.AddToClassList(USSEntry); await UniTask.NextFrame(); RootVisualElement.AddToClassList(USSEntryAsync); await UniTask.NextFrame(); await EntryAsync(); try { if (OnEntryAsync is not null) { await OnEntryAsync.UniTaskFunc(); } } catch (Exception e) { BIT4Log.LogException(e); } try { var cts = new CancellationTokenSource(); cts.CancelAfter(1000); await WaitUtilTransitionCompleted.Task.AttachExternalCancellation(cts.Token); } catch (OperationCanceledException) { } await UniTask.SwitchToMainThread(); RootVisualElement.AddToClassList(USSEntered); OnPanelEntry(); OnEntryCompleted?.Invoke(); RootVisualElement.RemoveFromClassList(USSEntry); RootVisualElement.RemoveFromClassList(USSEntryAsync); } private void OnTransitionEnd(TChangeEvent evt) { WaitUtilTransitionCompleted.TrySetResult(); } public virtual UniTask EntryAsync() { return UniTask.CompletedTask; } public override void OnStateExit(IState old, IState newState) { OnPanelExit(); InputActionGroup.allowInput.RemoveElement(this); OnExit?.Invoke(); } public override async UniTask OnStateExitAsync(IState old, IState newState) { RootVisualElement?.RemoveFromClassList(USSEntered); await UniTask.NextFrame(); RootVisualElement?.AddToClassList(USSExit); await UniTask.NextFrame(); RootVisualElement?.AddToClassList(USSExitAsync); await UniTask.NextFrame(); await OnExitAsync.UniTaskFunc(); try { var cts = new CancellationTokenSource(); cts.CancelAfter(1000); await WaitUtilTransitionCompleted.Task.AttachExternalCancellation(cts.Token); } catch (OperationCanceledException) { } RootVisualElement?.RemoveFromClassList(USSExit); RootVisualElement?.RemoveFromClassList(USSExitAsync); RootVisualElement?.AddToClassList(USSExited); RootVisualElement?.SetActive(false); if (AllowReload) { RootVisualElement?.RemoveFromHierarchy(); RootVisualElement = null; } OnExitCompleted?.Invoke(); } public event Action OnEntry; public event Func OnEntryAsync; public event Action OnEntryCompleted; public event Action OnExit; public event Func OnExitAsync; public event Action OnExitCompleted; public event Action OnInitiated; public event Func OnInitiatedAsync; public virtual void OnTick(float deltaTime) { } public virtual void Dispose() { InputActionGroup.Dispose(); RootVisualElement?.RemoveFromHierarchy(); IsDisposed = true; } } }