Files

158 lines
3.7 KiB
C#

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public static class UIBackStack
{
private sealed class Entry
{
public UnityEngine.Object owner;
public Func<bool> canHandle;
public Func<bool> handleBack;
public long sequence;
}
private static readonly List<Entry> entries = new List<Entry>();
private static long nextSequence;
private static bool hookedSceneEvents;
public static void RegisterOrBump(UnityEngine.Object owner, Func<bool> canHandle, Func<bool> handleBack)
{
if (owner == null || handleBack == null)
{
return;
}
EnsureSceneHook();
PurgeInvalidEntries();
for (int i = 0; i < entries.Count; i++)
{
Entry existing = entries[i];
if (ReferenceEquals(existing.owner, owner))
{
existing.canHandle = canHandle;
existing.handleBack = handleBack;
existing.sequence = ++nextSequence;
return;
}
}
entries.Add(new Entry
{
owner = owner,
canHandle = canHandle,
handleBack = handleBack,
sequence = ++nextSequence
});
}
public static void Unregister(UnityEngine.Object owner)
{
if (owner == null)
{
return;
}
for (int i = entries.Count - 1; i >= 0; i--)
{
if (ReferenceEquals(entries[i].owner, owner))
{
entries.RemoveAt(i);
}
}
}
public static bool TryHandleTop()
{
EnsureSceneHook();
PurgeInvalidEntries();
entries.Sort((a, b) => a.sequence.CompareTo(b.sequence));
for (int i = entries.Count - 1; i >= 0; i--)
{
Entry entry = entries[i];
if (!CanEntryHandle(entry))
{
continue;
}
try
{
if (entry.handleBack())
{
PurgeInvalidEntries();
return true;
}
}
catch (Exception ex)
{
Debug.LogWarning($"[UIBackStack] HandleBack failed: {ex.Message}");
}
}
PurgeInvalidEntries();
return false;
}
private static bool CanEntryHandle(Entry entry)
{
if (entry == null || entry.owner == null)
{
return false;
}
try
{
return entry.canHandle == null || entry.canHandle();
}
catch (Exception ex)
{
Debug.LogWarning($"[UIBackStack] CanHandle failed: {ex.Message}");
return false;
}
}
private static void PurgeInvalidEntries()
{
for (int i = entries.Count - 1; i >= 0; i--)
{
Entry entry = entries[i];
if (entry == null || entry.owner == null)
{
entries.RemoveAt(i);
continue;
}
if (entry.owner is Behaviour behaviour && behaviour.gameObject == null)
{
entries.RemoveAt(i);
continue;
}
if (entry.owner is GameObject ownerGo && ownerGo == null)
{
entries.RemoveAt(i);
}
}
}
private static void EnsureSceneHook()
{
if (hookedSceneEvents)
{
return;
}
hookedSceneEvents = true;
SceneManager.sceneLoaded += HandleSceneLoaded;
}
private static void HandleSceneLoaded(Scene scene, LoadSceneMode mode)
{
PurgeInvalidEntries();
}
}