using System; using System.Collections; using System.Collections.Generic; using System.Threading; using BITKit.Mod; using Cysharp.Threading.Tasks; using UnityEngine; using UnityEngine.Serialization; using UnityEngine.InputSystem; using UnityEngine.UIElements; // ReSharper disable MemberCanBeProtected.Global // ReSharper disable ClassWithVirtualMembersNeverInherited.Global // ReSharper disable UnusedMember.Global // ReSharper disable MemberCanBePrivate.Global namespace BITKit.UX { public abstract class UIToolKitPanel : IUXPanel { 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 UIToolKitPanel(IUXService uxService) { UXService = uxService; uxService.Register(this); InitializeAsync().Forget(); } private async UniTask InitializeAsync() { await _isBusy; using var b = _isBusy.GetHandle(); if (RootVisualElement is null) { VisualTreeAsset = await ModService.LoadAsset(DocumentPath); RootVisualElement = UXService.Root.As().Create(VisualTreeAsset); 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; var invisible = RootVisualElement.Create(); invisible.name = "invisible_return_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.SetActive(false); WaitUtilInitialized.TrySetResult(); } } protected virtual Optional EntryDuration { get; }= new(); protected virtual Optional ExitDuration { get; }= new(); protected static 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 bool IsEntered { get; set; } protected virtual void OnReturn() { UXService.Return(); } protected virtual void OnPanelEntry(){} protected virtual void OnPanelExit(){} void IEntryElement.Entry() { InputActionGroup.allowInput.AddElement(this); OnEntry?.Invoke(); } async UniTask IEntryElement.EntryAsync() { await InitializeAsync(); RootVisualElement.SetActive(true); RootVisualElement.AddToClassList(USSEntry); RootVisualElement.AddToClassList(USSEntryAsync); if (EntryDuration.Allow) { var task = EntryAsync(); var durationTask = UniTask.Delay(TimeSpan.FromSeconds(EntryDuration.Value)); await durationTask; RootVisualElement.RemoveFromClassList(USSEntry); RootVisualElement.RemoveFromClassList(USSEntryAsync); RootVisualElement.AddToClassList(USSEntered); await task; } else { await EntryAsync(); } try { if (OnEntryAsync is not null) { await OnEntryAsync.UniTaskFunc(); } } catch (Exception e) { BIT4Log.LogException(e); } } public virtual UniTask EntryAsync() { return UniTask.CompletedTask; } void IEntryElement.Entered() { OnPanelEntry(); OnEntryCompleted?.Invoke(); } void IEntryElement.Exit() { RootVisualElement?.AddToClassList(USSExit); OnPanelExit(); InputActionGroup.allowInput.RemoveElement(this); OnExit?.Invoke(); } async UniTask IEntryElement.ExitAsync() { RootVisualElement?.RemoveFromClassList(USSEntered); RootVisualElement?.AddToClassList(USSExitAsync); await OnExitAsync.UniTaskFunc(); if (EntryDuration.Allow is false) return; await UniTask.Delay(TimeSpan.FromSeconds(EntryDuration.Value)); } void IEntryElement.Exited() { RootVisualElement?.RemoveFromClassList(USSExit); RootVisualElement?.RemoveFromClassList(USSExitAsync); RootVisualElement?.AddToClassList(USSEntry); 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 virtual void OnTick(float deltaTime) { } } }