From 4456431f250c697ee371f98b44554e68f7e60ec4 Mon Sep 17 00:00:00 2001 From: rowan Date: Sat, 19 Jul 2025 23:42:43 -0400 Subject: [PATCH] fixes #8 --- Assets/SOAP/UI/Fade In.asset | 4 +- Assets/SOAP/UI/Fade Out.asset | 25 +++ Assets/SOAP/UI/Fade Out.asset.meta | 8 + Assets/SOAP/UI/Modal.asset | 4 +- Assets/Scenes/SampleScene.unity | 35 ++++- Assets/Scripts/Extension/VisualElement.cs | 19 +++ .../Scripts/Extension/VisualElement.cs.meta | 2 + Assets/Scripts/Interaction/IInteractable.cs | 2 + Assets/Scripts/Interaction/Interactable.cs | 9 ++ Assets/Scripts/Interaction/Interactor.cs | 2 + Assets/Scripts/Rendering/BillboardManager.cs | 3 - Assets/Scripts/System/Duration.cs | 3 + Assets/Scripts/System/SerializableDuration.cs | 7 +- .../Scripts/UI/Effect/AddTransitionEffect.cs | 2 +- Assets/Scripts/UI/Effect/FadeEffect.cs | 36 +++-- Assets/Scripts/UI/{ => Elements}/UiElement.cs | 0 .../UI/{ => Elements}/UiElement.cs.meta | 0 .../UI/Orchestration/UiElementInstance.cs | 143 ++++++++++++++++++ .../UiElementInstance.cs.meta | 0 .../UI/Orchestration/UiSceneManager.cs | 59 ++++---- Assets/Scripts/UI/TestThing.cs | 27 +++- Assets/Scripts/UI/UiElementInstance.cs | 71 --------- 22 files changed, 334 insertions(+), 127 deletions(-) create mode 100644 Assets/SOAP/UI/Fade Out.asset create mode 100644 Assets/SOAP/UI/Fade Out.asset.meta create mode 100644 Assets/Scripts/Extension/VisualElement.cs create mode 100644 Assets/Scripts/Extension/VisualElement.cs.meta rename Assets/Scripts/UI/{ => Elements}/UiElement.cs (100%) rename Assets/Scripts/UI/{ => Elements}/UiElement.cs.meta (100%) create mode 100644 Assets/Scripts/UI/Orchestration/UiElementInstance.cs rename Assets/Scripts/UI/{ => Orchestration}/UiElementInstance.cs.meta (100%) delete mode 100644 Assets/Scripts/UI/UiElementInstance.cs diff --git a/Assets/SOAP/UI/Fade In.asset b/Assets/SOAP/UI/Fade In.asset index 14da527..4e9e487 100644 --- a/Assets/SOAP/UI/Fade In.asset +++ b/Assets/SOAP/UI/Fade In.asset @@ -15,11 +15,11 @@ MonoBehaviour: addTransition: 1 propertyName: opacity duration: - duration: 0.3 + duration: 1 unit: 2 easing: 3 delay: - duration: 0 + duration: 1 unit: 2 from: 0 to: 1 diff --git a/Assets/SOAP/UI/Fade Out.asset b/Assets/SOAP/UI/Fade Out.asset new file mode 100644 index 0000000..a4e04bf --- /dev/null +++ b/Assets/SOAP/UI/Fade Out.asset @@ -0,0 +1,25 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 94cc50e7b5f20a817aef3384d0889279, type: 3} + m_Name: Fade Out + m_EditorClassIdentifier: + addTransition: 1 + propertyName: opacity + duration: + duration: 1 + unit: 2 + easing: 3 + delay: + duration: 1 + unit: 2 + from: 1 + to: 0 diff --git a/Assets/SOAP/UI/Fade Out.asset.meta b/Assets/SOAP/UI/Fade Out.asset.meta new file mode 100644 index 0000000..fdc609b --- /dev/null +++ b/Assets/SOAP/UI/Fade Out.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fd3397ffea6cbccc5992ef2e70a2ed44 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/SOAP/UI/Modal.asset b/Assets/SOAP/UI/Modal.asset index 184698c..67464f8 100644 --- a/Assets/SOAP/UI/Modal.asset +++ b/Assets/SOAP/UI/Modal.asset @@ -14,7 +14,9 @@ MonoBehaviour: m_EditorClassIdentifier: visualTreeAsset: {fileID: 9197481963319205126, guid: bc70bdaaf37609283aac14435c5441a2, type: 3} effects: - - timing: 0 + - uiEvent: 0 effect: {fileID: 11400000, guid: 0902dc21238be2bb3ae758e1d5218922, type: 2} + - uiEvent: 1 + effect: {fileID: 11400000, guid: fd3397ffea6cbccc5992ef2e70a2ed44, type: 2} titleId: title contentId: content diff --git a/Assets/Scenes/SampleScene.unity b/Assets/Scenes/SampleScene.unity index 7445147..f976dc7 100644 --- a/Assets/Scenes/SampleScene.unity +++ b/Assets/Scenes/SampleScene.unity @@ -922,6 +922,9 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: oneTimeUse: 0 + onTarget: + m_PersistentCalls: + m_Calls: [] onInteracted: m_PersistentCalls: m_Calls: [] @@ -1211,7 +1214,6 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: camera: {fileID: 330585546} - registerChildren: 1 --- !u!114 &1281046470 MonoBehaviour: m_ObjectHideFlags: 0 @@ -1227,6 +1229,9 @@ MonoBehaviour: orchestrator: {fileID: 11400000, guid: 994ab04db2c2004abb8272830cb16b26, type: 2} root: {fileID: 1281046468} panelSettings: {fileID: 11400000, guid: 2bc58aab5867867e5b0feeae2df42fd0, type: 2} + despawnTimeout: + duration: 2 + unit: 2 prefab: {fileID: 0} --- !u!1 &1363717994 GameObject: @@ -2323,6 +2328,34 @@ PrefabInstance: propertyPath: oneTimeUse value: 1 objectReference: {fileID: 0} + - target: {fileID: 2022912019066975223, guid: 51202e3c62c3d3e1c9bb0ff9c09a608f, type: 3} + propertyPath: onTarget.m_PersistentCalls.m_Calls.Array.size + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2022912019066975223, guid: 51202e3c62c3d3e1c9bb0ff9c09a608f, type: 3} + propertyPath: onTarget.m_PersistentCalls.m_Calls.Array.data[0].m_Mode + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2022912019066975223, guid: 51202e3c62c3d3e1c9bb0ff9c09a608f, type: 3} + propertyPath: onTarget.m_PersistentCalls.m_Calls.Array.data[0].m_Target + value: + objectReference: {fileID: 1740748263} + - target: {fileID: 2022912019066975223, guid: 51202e3c62c3d3e1c9bb0ff9c09a608f, type: 3} + propertyPath: onTarget.m_PersistentCalls.m_Calls.Array.data[0].m_CallState + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 2022912019066975223, guid: 51202e3c62c3d3e1c9bb0ff9c09a608f, type: 3} + propertyPath: onTarget.m_PersistentCalls.m_Calls.Array.data[0].m_MethodName + value: Toggle + objectReference: {fileID: 0} + - target: {fileID: 2022912019066975223, guid: 51202e3c62c3d3e1c9bb0ff9c09a608f, type: 3} + propertyPath: onTarget.m_PersistentCalls.m_Calls.Array.data[0].m_TargetAssemblyTypeName + value: KitsuneCafe.UI.TestThing, Assembly-CSharp + objectReference: {fileID: 0} + - target: {fileID: 2022912019066975223, guid: 51202e3c62c3d3e1c9bb0ff9c09a608f, type: 3} + propertyPath: onTarget.m_PersistentCalls.m_Calls.Array.data[0].m_Arguments.m_ObjectArgumentAssemblyTypeName + value: UnityEngine.Object, UnityEngine + objectReference: {fileID: 0} - target: {fileID: 2368094401510200604, guid: 51202e3c62c3d3e1c9bb0ff9c09a608f, type: 3} propertyPath: m_LocalPosition.x value: -2 diff --git a/Assets/Scripts/Extension/VisualElement.cs b/Assets/Scripts/Extension/VisualElement.cs new file mode 100644 index 0000000..629d127 --- /dev/null +++ b/Assets/Scripts/Extension/VisualElement.cs @@ -0,0 +1,19 @@ +using System.Threading; +using R3; +using UnityEngine.UIElements; + +namespace KitsuneCafe.Extension +{ + public static class VisualElementExtensions + { + public static Observable ObserveEvent(this VisualElement target, CancellationToken token = default) where T : EventBase, new() + { + return Observable.FromEvent, T>( + h => e => h(e), + e => target.RegisterCallback(e), + e => target.UnregisterCallback(e), + token + ); + } + } +} diff --git a/Assets/Scripts/Extension/VisualElement.cs.meta b/Assets/Scripts/Extension/VisualElement.cs.meta new file mode 100644 index 0000000..69a3e60 --- /dev/null +++ b/Assets/Scripts/Extension/VisualElement.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: aaf4cd39f81ac6dd8b5949c0f4a2f7ef \ No newline at end of file diff --git a/Assets/Scripts/Interaction/IInteractable.cs b/Assets/Scripts/Interaction/IInteractable.cs index 226ad8b..9bb83aa 100644 --- a/Assets/Scripts/Interaction/IInteractable.cs +++ b/Assets/Scripts/Interaction/IInteractable.cs @@ -80,6 +80,8 @@ namespace KitsuneCafe.Interaction } bool IsInteractable { get; } + + virtual void Target(IInteractor interactor) { } IResult Interact(IInteractor interactor); } } \ No newline at end of file diff --git a/Assets/Scripts/Interaction/Interactable.cs b/Assets/Scripts/Interaction/Interactable.cs index 32dd283..47e1d7d 100644 --- a/Assets/Scripts/Interaction/Interactable.cs +++ b/Assets/Scripts/Interaction/Interactable.cs @@ -9,6 +9,9 @@ namespace KitsuneCafe.Interaction [SerializeField] private bool oneTimeUse = false; + [SerializeField] + private UnityEvent onTarget = default; + [SerializeField] private UnityEvent onInteracted = default; @@ -16,6 +19,12 @@ namespace KitsuneCafe.Interaction public bool IsInteractable => isInteractable; + + void IInteractable.Target(IInteractor interactor) + { + onTarget.Invoke(interactor); + } + public IResult Interact(IInteractor interactor) { onInteracted.Invoke(interactor); diff --git a/Assets/Scripts/Interaction/Interactor.cs b/Assets/Scripts/Interaction/Interactor.cs index 61b2bf6..2f027b7 100644 --- a/Assets/Scripts/Interaction/Interactor.cs +++ b/Assets/Scripts/Interaction/Interactor.cs @@ -169,6 +169,8 @@ namespace KitsuneCafe.Player { if (interactable == null) { return; } + interactable.Target(this); + if (!interactable.TryGetComponent(out Outline outline)) { outline = interactable.gameObject.AddComponent(); diff --git a/Assets/Scripts/Rendering/BillboardManager.cs b/Assets/Scripts/Rendering/BillboardManager.cs index e9a3820..a1143e9 100644 --- a/Assets/Scripts/Rendering/BillboardManager.cs +++ b/Assets/Scripts/Rendering/BillboardManager.cs @@ -8,9 +8,6 @@ namespace KitsuneCafe.Rendering [SerializeField] private new Transform camera; - [SerializeField] - private bool registerChildren = false; - private readonly List transforms = new(); private void Reset() diff --git a/Assets/Scripts/System/Duration.cs b/Assets/Scripts/System/Duration.cs index 73874fd..ed13470 100644 --- a/Assets/Scripts/System/Duration.cs +++ b/Assets/Scripts/System/Duration.cs @@ -1,4 +1,5 @@ using System; +using UnityEngine.UIElements; namespace KitsuneCafe.System { @@ -26,8 +27,10 @@ namespace KitsuneCafe.System public Duration(long ticks) : this(ticks, TimeUnit.Ticks) { } public static implicit operator TimeSpan(Duration duration) => duration.AsTimeSpan(); + public static implicit operator TimeValue(Duration duration) => duration.AsTimeValue(); public readonly TimeSpan AsTimeSpan() => new(Value); + public readonly TimeValue AsTimeValue() => new(Into(TimeUnit.Seconds)); public static Duration From(long value, TimeUnit unit) { diff --git a/Assets/Scripts/System/SerializableDuration.cs b/Assets/Scripts/System/SerializableDuration.cs index aaff7e5..790fc1a 100644 --- a/Assets/Scripts/System/SerializableDuration.cs +++ b/Assets/Scripts/System/SerializableDuration.cs @@ -15,10 +15,7 @@ namespace KitsuneCafe.System public static implicit operator Duration(SerializableDuration d) => Duration.From(d.duration, d.unit); - public static implicit operator TimeValue(SerializableDuration d) - { - Duration duration = d; - return new TimeValue(duration.Into(TimeUnit.Seconds)); - } + public static implicit operator TimeSpan(SerializableDuration d) => Duration.From(d.duration, d.unit); + public static implicit operator TimeValue(SerializableDuration d) => Duration.From(d.duration, d.unit); } } diff --git a/Assets/Scripts/UI/Effect/AddTransitionEffect.cs b/Assets/Scripts/UI/Effect/AddTransitionEffect.cs index 61bf43a..2796f04 100644 --- a/Assets/Scripts/UI/Effect/AddTransitionEffect.cs +++ b/Assets/Scripts/UI/Effect/AddTransitionEffect.cs @@ -27,7 +27,7 @@ namespace KitsuneCafe.UI return new StyleList(new List { value }); } - public Observable Execute(VisualElement target, CancellationToken token) + public Observable Execute(VisualElement target, CancellationToken token) { target.style.transitionProperty = ToStyleList(PropertyName); target.style.transitionDuration = ToStyleList(Duration); diff --git a/Assets/Scripts/UI/Effect/FadeEffect.cs b/Assets/Scripts/UI/Effect/FadeEffect.cs index 2e22402..270e352 100644 --- a/Assets/Scripts/UI/Effect/FadeEffect.cs +++ b/Assets/Scripts/UI/Effect/FadeEffect.cs @@ -4,6 +4,8 @@ using KitsuneCafe.System.Attributes; using R3; using UnityEngine; using UnityEngine.UIElements; +using KitsuneCafe.Extension; +using Unit = R3.Unit; namespace KitsuneCafe.UI { @@ -55,25 +57,35 @@ namespace KitsuneCafe.UI To = to; } - private void DoTransition(VisualElement target) + private Observable ObserveGeometryChange(VisualElement target, CancellationToken token) { - target.style.opacity = From; - target.style.opacity = To; + return target.ObserveEvent(token) + .OnErrorResumeAsFailure() + .AsUnitObservable(); } + private Observable Defer(VisualElement target, CancellationToken token) + { + return Observable.Merge( + ObserveGeometryChange(target, token), + Observable.EveryUpdate(UnityFrameProvider.PostLateUpdate, token) + ) + .Where(_ => target.resolvedStyle.width > 0 && target.resolvedStyle.height > 0) + .Take(1); + } - public Observable Execute(VisualElement target, CancellationToken token) + public Observable Execute(VisualElement target, CancellationToken token) { target.style.opacity = From; - var to = To; - return Observable.NextFrame(cancellationToken: token) - .Do(_ => - { - target.style.opacity = to; - }) - .AsUnitObservable(); + return Defer(target, token) + .Do(_ => target.style.opacity = to) + .Select(_ => target.ObserveEvent()) + .Switch() + .Where(evt => evt.stylePropertyNames.Contains("opacity")) + .Take(1) + .TakeUntil(token) + .AsUnitObservable(); } - } } diff --git a/Assets/Scripts/UI/UiElement.cs b/Assets/Scripts/UI/Elements/UiElement.cs similarity index 100% rename from Assets/Scripts/UI/UiElement.cs rename to Assets/Scripts/UI/Elements/UiElement.cs diff --git a/Assets/Scripts/UI/UiElement.cs.meta b/Assets/Scripts/UI/Elements/UiElement.cs.meta similarity index 100% rename from Assets/Scripts/UI/UiElement.cs.meta rename to Assets/Scripts/UI/Elements/UiElement.cs.meta diff --git a/Assets/Scripts/UI/Orchestration/UiElementInstance.cs b/Assets/Scripts/UI/Orchestration/UiElementInstance.cs new file mode 100644 index 0000000..d159821 --- /dev/null +++ b/Assets/Scripts/UI/Orchestration/UiElementInstance.cs @@ -0,0 +1,143 @@ +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(); + } + + /// + /// Initial configuration of the UIDocument. Called by UISceneManager on spawn/reuse. + /// + public void Configure(IUiElement element, PanelSettings settings) + { + uiElement = element; + + cts = new(); + + Document.visualTreeAsset = element.VisualTreeAsset; + Document.panelSettings = settings; + + instance = ConfigureWhenReady(element) + .Subscribe(); + } + + /// + /// Internal configuration of the VisualElement. Called when rootVisualElement is ready. + /// + private void Configure(IUiElement uiElement, VisualElement visualElement) + { + this.visualElement = visualElement; + + uiElement.Configure(visualElement); + + Raise(UiEvent.OnCreate, cts.Token) + .Subscribe() + .AddTo(ref activeEffects); + } + + /// + /// Waits for the UIDocument's rootVisualElement to be ready. + /// + private Observable ConfigureWhenReady(IUiElement element) + { + return Observable.EveryValueChanged(Document, d => d.rootVisualElement) + .WhereNotNull() + .Take(1) + .TakeUntil(cts.Token) + .Do(root => Configure(element, root)) + .AsUnitObservable(); + } + + /// + /// Raises effects for a given UI event, returning an Observable that completes when all effects are done. + /// + private Observable Raise(UiEvent evt, CancellationToken token = default) + { + var effects = uiElement.GetEffectsFor(evt).ToArray(); + var obs = new List>(effects.Length); + + foreach (var effect in effects) + { + if (effect != null) + { + obs.Add(effect + .Instantiate() + .Execute(visualElement, token)); + } + } + + return Observable.Merge(obs); + } + + /// + /// Forcefully disposes of all active effects and instance lifecycle subscriptions. + /// This is for internal cleanup. It does NOT release the object to the pool. + /// + public void Dispose() + { + cts?.Cancel(); + cts?.Dispose(); + cts = null; + + // activeEffects.Dispose(); + activeEffects.Clear(); + + instance?.Dispose(); + + uiElement = default; + visualElement = default; + } + + /// + /// Initiates the despawn process: runs OnDestroy effects, waits for them to finish, + /// then disposes everything and releases the object back to the pool. + /// + public Observable 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(); + } + + /// + /// Immediately disposes of effects and releases the object to the pool, + /// without awaiting OnDestroy effects. + /// + public void DespawnNow() + { + Dispose(); + PooledObject.Release(); + } + } +} diff --git a/Assets/Scripts/UI/UiElementInstance.cs.meta b/Assets/Scripts/UI/Orchestration/UiElementInstance.cs.meta similarity index 100% rename from Assets/Scripts/UI/UiElementInstance.cs.meta rename to Assets/Scripts/UI/Orchestration/UiElementInstance.cs.meta diff --git a/Assets/Scripts/UI/Orchestration/UiSceneManager.cs b/Assets/Scripts/UI/Orchestration/UiSceneManager.cs index 8cb494d..1c02bc9 100644 --- a/Assets/Scripts/UI/Orchestration/UiSceneManager.cs +++ b/Assets/Scripts/UI/Orchestration/UiSceneManager.cs @@ -5,7 +5,6 @@ using System; using UnityEngine.UIElements; using UnityEngine.Pool; using KitsuneCafe.System; -using KitsuneCafe.Extension; namespace KitsuneCafe.UI { @@ -15,6 +14,7 @@ namespace KitsuneCafe.UI [SerializeField] private Transform root; [SerializeField] private PanelSettings panelSettings; + [SerializeField] private SerializableDuration despawnTimeout; [SerializeField] private GameObject prefab; private readonly Dictionary activeElements = new(); @@ -74,17 +74,24 @@ namespace KitsuneCafe.UI } else { - obj = new GameObject("Pooled Object", typeof(UIDocument)); + obj = new GameObject( + "Pooled Object", + typeof(UIDocument), + typeof(PooledObject), + typeof(UiElementInstance) + ); } - obj.AddComponent(); - var doc = obj.GetComponent(); doc.panelSettings = panelSettings; - var poolObj = obj.AddComponent(); + var poolObj = obj.GetComponent(); poolObj.objectPool = pool; + var instance = obj.GetComponent(); + instance.Document = doc; + instance.PooledObject = poolObj; + obj.transform.SetParent(root); return obj; @@ -110,47 +117,41 @@ namespace KitsuneCafe.UI return GetOrCreatePool(element).Get(); } - private Observable ObserveEvent(VisualElement target) where T : EventBase, new() - { - return Observable.FromEvent, T>( - h => e => h(e), - e => target.RegisterCallback(e), - e => target.UnregisterCallback(e) - ); - } - private void SpawnElement(ElementId id, IUiElement element, Vector3 worldPosition) { var obj = GetUiDocument(element); + activeElements[id] = obj; var trans = obj.transform; trans.SetParent(root); trans.SetPositionAndRotation(worldPosition, Quaternion.identity); trans.localScale = Vector3.one; - if (obj.TryGetComponent(out UIDocument doc) && obj.TryGetComponent(out UiElementInstance instance)) + if (obj.TryGetComponent(out UiElementInstance instance)) { - doc.visualTreeAsset = element.VisualTreeAsset; - doc.panelSettings = panelSettings; - Observable.EveryValueChanged(doc, d => d.rootVisualElement) - .WhereNotNull() - .Select(_ => doc) - .Subscribe(doc => - { - instance.Configure(element, doc.rootVisualElement); - }) - .AddTo(doc.gameObject); + instance.Configure(element, panelSettings); } - - activeElements[id] = obj; } + // FIXME: track which pool each active element belongs to? private void DespawnElement(ElementId id) { if (activeElements.TryGetValue(id, out GameObject obj)) { - obj.GetComponent().Release(); - activeElements.Remove(id); + if (obj.TryGetComponent(out var instance)) + { + instance.Despawn() + .Race(Observable.Timer(despawnTimeout).Do(_ => instance.DespawnNow())) + .Do(onCompleted: _ => activeElements.Remove(id)) + .Subscribe() + .AddTo(obj); + + } + else if (obj.TryGetComponent(out PooledObject poolObj)) + { + poolObj.Release(); + activeElements.Remove(id); + } } } } diff --git a/Assets/Scripts/UI/TestThing.cs b/Assets/Scripts/UI/TestThing.cs index e37401b..4c86445 100644 --- a/Assets/Scripts/UI/TestThing.cs +++ b/Assets/Scripts/UI/TestThing.cs @@ -10,9 +10,32 @@ namespace KitsuneCafe.UI [SerializeField] private ModalElement modal; - private void Start() + private ElementId id; + + public void Open() { - orchestrator.SpawnElement(modal.Create("test", "fuck !!!! :3"), transform.position + Vector3.up); + id = orchestrator.SpawnElement(modal.Create("<3", "fuck !!!! :3"), transform.position + (Vector3.up * 2)); + } + + public void Close() + { + if (id != default) + { + orchestrator.DespawnElement(id); + id = default; + } + } + + public void Toggle() + { + if (id != default) + { + Close(); + } + else + { + Open(); + } } } } diff --git a/Assets/Scripts/UI/UiElementInstance.cs b/Assets/Scripts/UI/UiElementInstance.cs deleted file mode 100644 index aa0ebe8..0000000 --- a/Assets/Scripts/UI/UiElementInstance.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using R3; -using UnityEngine; -using UnityEngine.UIElements; - -namespace KitsuneCafe.UI -{ - public class UiElementInstance : MonoBehaviour, IDisposable - { - private IUiElement uiElement; - private VisualElement visualElement; - - private readonly CompositeDisposable activeEffects = new(); - - private bool configured = false; - - public void Configure(IUiElement uiElement, VisualElement visualElement) - { - configured = true; - this.uiElement = uiElement; - this.visualElement = visualElement; - - activeEffects.Clear(); - - uiElement.Configure(visualElement); - - Raise(UiEvent.OnCreate); - } - - private void Raise(UiEvent evt) - { - foreach (var effect in uiElement.GetEffectsFor(evt)) - { - // using explicit null comparison because of the way - // GameObjects handle being null - if (effect != null) - { - effect.Instantiate() - .Execute(visualElement, destroyCancellationToken) - .Subscribe() - .AddTo(activeEffects); - } - } - } - - public void Dispose() - { - if (!configured) { return; } - - Raise(UiEvent.OnDestroy); - - activeEffects.Dispose(); - activeEffects.Clear(); - - uiElement = default; - visualElement = default; - - configured = false; - } - - private void OnDisable() - { - Dispose(); - } - - private void OnDestroy() - { - Dispose(); - } - } -}