143 lines
4.2 KiB
C#
143 lines
4.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using KitsuneCafe.System;
|
|
using R3;
|
|
using UnityEngine;
|
|
using UnityEngine.UIElements;
|
|
using Unit = R3.Unit;
|
|
|
|
namespace KitsuneCafe.UI
|
|
{
|
|
public class UiElementInstance : MonoBehaviour, IDisposable
|
|
{
|
|
public UIDocument Document;
|
|
public PooledObject PooledObject;
|
|
|
|
private IUiElement uiElement;
|
|
private VisualElement visualElement;
|
|
|
|
private DisposableBag activeEffects;
|
|
private IDisposable instance;
|
|
|
|
private CancellationTokenSource cts;
|
|
|
|
private void OnDisable()
|
|
{
|
|
Dispose();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initial configuration of the UIDocument. Called by UISceneManager on spawn/reuse.
|
|
/// </summary>
|
|
public void Configure(IUiElement element, PanelSettings settings)
|
|
{
|
|
uiElement = element;
|
|
|
|
cts = new();
|
|
|
|
Document.visualTreeAsset = element.VisualTreeAsset;
|
|
Document.panelSettings = settings;
|
|
|
|
instance = ConfigureWhenReady(element)
|
|
.Subscribe();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Internal configuration of the VisualElement. Called when rootVisualElement is ready.
|
|
/// </summary>
|
|
private void Configure(IUiElement uiElement, VisualElement visualElement)
|
|
{
|
|
this.visualElement = visualElement;
|
|
|
|
uiElement.Configure(visualElement);
|
|
|
|
Raise(UiEvent.OnCreate, cts.Token)
|
|
.Subscribe()
|
|
.AddTo(ref activeEffects);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Waits for the UIDocument's rootVisualElement to be ready.
|
|
/// </summary>
|
|
private Observable<Unit> ConfigureWhenReady(IUiElement element)
|
|
{
|
|
return Observable.EveryValueChanged(Document, d => d.rootVisualElement)
|
|
.WhereNotNull()
|
|
.Take(1)
|
|
.TakeUntil(cts.Token)
|
|
.Do(root => Configure(element, root))
|
|
.AsUnitObservable();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raises effects for a given UI event, returning an Observable that completes when all effects are done.
|
|
/// </summary>
|
|
private Observable<Unit> Raise(UiEvent evt, CancellationToken token = default)
|
|
{
|
|
var effects = uiElement.GetEffectsFor(evt).ToArray();
|
|
var obs = new List<Observable<Unit>>(effects.Length);
|
|
|
|
foreach (var effect in effects)
|
|
{
|
|
if (effect != null)
|
|
{
|
|
obs.Add(effect
|
|
.Instantiate()
|
|
.Execute(visualElement, token));
|
|
}
|
|
}
|
|
|
|
return Observable.Merge(obs);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Forcefully disposes of all active effects and instance lifecycle subscriptions.
|
|
/// This is for internal cleanup. It does NOT release the object to the pool.
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
cts?.Cancel();
|
|
cts?.Dispose();
|
|
cts = null;
|
|
|
|
// activeEffects.Dispose();
|
|
activeEffects.Clear();
|
|
|
|
instance?.Dispose();
|
|
|
|
uiElement = default;
|
|
visualElement = default;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initiates the despawn process: runs OnDestroy effects, waits for them to finish,
|
|
/// then disposes everything and releases the object back to the pool.
|
|
/// </summary>
|
|
public Observable<Unit> Despawn(CancellationTokenSource cts = default)
|
|
{
|
|
cts ??= new CancellationTokenSource();
|
|
|
|
return Raise(UiEvent.OnDestroy, cts.Token)
|
|
.Do(onCompleted: _ =>
|
|
{
|
|
Dispose();
|
|
PooledObject.Release();
|
|
},
|
|
onErrorResume: _ => cts.Cancel())
|
|
.TakeUntil(cts.Token)
|
|
.AsUnitObservable();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Immediately disposes of effects and releases the object to the pool,
|
|
/// without awaiting OnDestroy effects.
|
|
/// </summary>
|
|
public void DespawnNow()
|
|
{
|
|
Dispose();
|
|
PooledObject.Release();
|
|
}
|
|
}
|
|
}
|