using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Threading; using AlicizaX; using Cysharp.Threading.Tasks; namespace AlicizaX.UI.Runtime { sealed class LayerData { public readonly List OrderList; public readonly Dictionary IndexMap; public int LastFullscreenIndex; public LayerData(int initialCapacity) { OrderList = new List(initialCapacity); IndexMap = new Dictionary(initialCapacity); LastFullscreenIndex = -1; } } internal sealed partial class UIService { private readonly LayerData[] _openUI = new LayerData[(int)UILayer.All]; private async UniTask ShowUIImplAsync(UIMetadata metaInfo, params object[] userDatas) { CreateMetaUI(metaInfo); await UIHolderFactory.CreateUIResourceAsync(metaInfo, UICacheLayer); if (metaInfo.View == null || metaInfo.State == UIState.Uninitialized || metaInfo.State == UIState.Destroyed) { return null; } FinalizeShow(metaInfo, userDatas); await UpdateVisualState(metaInfo, metaInfo.CancellationToken); return metaInfo.View; } private UIBase ShowUIImplSync(UIMetadata metaInfo, params object[] userDatas) { CreateMetaUI(metaInfo); UIHolderFactory.CreateUIResourceSync(metaInfo, UICacheLayer); if (metaInfo.View == null || metaInfo.State == UIState.Uninitialized || metaInfo.State == UIState.Destroyed) { return null; } FinalizeShow(metaInfo, userDatas); UpdateVisualState(metaInfo).Forget(); return metaInfo.View; } private async UniTask CloseUIImpl(UIMetadata meta, bool force) { if (meta.State == UIState.Uninitialized) { return; } if (meta.State == UIState.CreatedUI) { meta.CancelAsyncOperations(); meta.Dispose(); return; } if (meta.State == UIState.Loaded || meta.State == UIState.Initialized) { meta.CancelAsyncOperations(); var popResult = Pop(meta); SortWindowVisible(meta.MetaInfo.UILayer, popResult.previousFullscreenIndex); SortWindowDepth(meta.MetaInfo.UILayer, popResult.removedIndex >= 0 ? popResult.removedIndex : 0); meta.View.Visible = false; CacheWindow(meta, force); return; } meta.CancelAsyncOperations(); await meta.View.InternalClose(); if (meta.State != UIState.Closed) { return; } var closedPopResult = Pop(meta); SortWindowVisible(meta.MetaInfo.UILayer, closedPopResult.previousFullscreenIndex); SortWindowDepth(meta.MetaInfo.UILayer, closedPopResult.removedIndex >= 0 ? closedPopResult.removedIndex : 0); CacheWindow(meta, force); } private UIBase GetUIImpl(UIMetadata meta) { return meta.View; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void CreateMetaUI(UIMetadata meta) { if (meta.State == UIState.Uninitialized) meta.CreateUI(); } private void FinalizeShow(UIMetadata meta, object[] userDatas) { if (meta.InCache) { RemoveFromCache(meta.MetaInfo.RuntimeTypeHandle); Push(meta); } else { switch (meta.State) { case UIState.Loaded: Push(meta); break; case UIState.Opening: case UIState.Closing: case UIState.Opened: MoveToTop(meta); break; } } meta.View.RefreshParams(userDatas); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Push(UIMetadata meta) { var layer = _openUI[meta.MetaInfo.UILayer]; if (!layer.IndexMap.ContainsKey(meta.MetaInfo.RuntimeTypeHandle)) { int index = layer.OrderList.Count; layer.OrderList.Add(meta); layer.IndexMap[meta.MetaInfo.RuntimeTypeHandle] = index; if (meta.MetaInfo.FullScreen) { layer.LastFullscreenIndex = index; } UpdateLayerParent(meta); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private (int removedIndex, int previousFullscreenIndex) Pop(UIMetadata meta) { var layer = _openUI[meta.MetaInfo.UILayer]; int previousFullscreenIndex = layer.LastFullscreenIndex; if (layer.IndexMap.TryGetValue(meta.MetaInfo.RuntimeTypeHandle, out int index)) { layer.OrderList.RemoveAt(index); layer.IndexMap.Remove(meta.MetaInfo.RuntimeTypeHandle); for (int i = index; i < layer.OrderList.Count; i++) { var item = layer.OrderList[i]; layer.IndexMap[item.MetaInfo.RuntimeTypeHandle] = i; } UpdateFullscreenIndexAfterRemove(layer, meta, index); return (index, previousFullscreenIndex); } return (-1, previousFullscreenIndex); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void UpdateLayerParent(UIMetadata meta) { if (meta.View?.Holder != null && meta.View.Holder.IsValid()) { var layerRect = GetLayerRect(meta.MetaInfo.UILayer); meta.View.Holder.transform.SetParent(layerRect); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void MoveToTop(UIMetadata meta) { var layer = _openUI[meta.MetaInfo.UILayer]; int lastIdx = layer.OrderList.Count - 1; if (!layer.IndexMap.TryGetValue(meta.MetaInfo.RuntimeTypeHandle, out int currentIdx)) return; if (currentIdx != lastIdx && currentIdx >= 0) { layer.OrderList.RemoveAt(currentIdx); layer.OrderList.Add(meta); for (int i = currentIdx; i < lastIdx; i++) { var item = layer.OrderList[i]; layer.IndexMap[item.MetaInfo.RuntimeTypeHandle] = i; } layer.IndexMap[meta.MetaInfo.RuntimeTypeHandle] = lastIdx; UpdateFullscreenIndexAfterMove(layer, meta, currentIdx, lastIdx); SortWindowDepth(meta.MetaInfo.UILayer, currentIdx); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private async UniTask UpdateVisualState(UIMetadata meta, CancellationToken cancellationToken = default) { SortWindowVisible(meta.MetaInfo.UILayer); SortWindowDepth(meta.MetaInfo.UILayer); if (meta.State == UIState.Loaded) { await meta.View.InternalInitlized(cancellationToken); } await meta.View.InternalOpen(cancellationToken); } private void SortWindowVisible(int layer, int previousFullscreenIndex = int.MinValue) { var layerData = _openUI[layer]; var list = layerData.OrderList; int count = list.Count; int fullscreenIdx = layerData.LastFullscreenIndex; if (fullscreenIdx >= count || (fullscreenIdx >= 0 && !IsDisplayFullscreen(list[fullscreenIdx]))) { fullscreenIdx = FindLastFullscreenIndex(list, count - 1); layerData.LastFullscreenIndex = fullscreenIdx; } int oldFullscreenIndex = previousFullscreenIndex == int.MinValue ? fullscreenIdx : previousFullscreenIndex; if (oldFullscreenIndex == fullscreenIdx) { ApplyVisibilityRange(list, fullscreenIdx >= 0 ? fullscreenIdx : 0, count, fullscreenIdx); return; } if (oldFullscreenIndex == -1 && fullscreenIdx == -1) { ApplyVisibilityRange(list, 0, count, -1); return; } int start = oldFullscreenIndex < 0 || fullscreenIdx < 0 ? 0 : Math.Min(oldFullscreenIndex, fullscreenIdx); int endExclusive = oldFullscreenIndex < 0 || fullscreenIdx < 0 ? count : Math.Max(oldFullscreenIndex, fullscreenIdx) + 1; ApplyVisibilityRange(list, start, endExclusive, fullscreenIdx); } private void SortWindowDepth(int layer, int startIndex = 0) { var list = _openUI[layer].OrderList; int baseDepth = layer * LAYER_DEEP; for (int i = startIndex; i < list.Count; i++) { int newDepth = baseDepth + i * WINDOW_DEEP; if (list[i].View.Depth != newDepth) { list[i].View.Depth = newDepth; } } } private static bool IsDisplayFullscreen(UIMetadata meta) { return meta.MetaInfo.FullScreen && UIStateMachine.IsDisplayActive(meta.State); } private static int FindLastFullscreenIndex(List list, int startIndex) { for (int i = Math.Min(startIndex, list.Count - 1); i >= 0; i--) { if (IsDisplayFullscreen(list[i])) { return i; } } return -1; } private static void ApplyVisibilityRange(List list, int startInclusive, int endExclusive, int fullscreenIdx) { if (startInclusive < 0) { startInclusive = 0; } if (endExclusive > list.Count) { endExclusive = list.Count; } bool showAll = fullscreenIdx < 0; for (int i = startInclusive; i < endExclusive; i++) { list[i].View.Visible = showAll || i >= fullscreenIdx; } } private static void UpdateFullscreenIndexAfterRemove(LayerData layer, UIMetadata removedMeta, int removedIndex) { if (layer.OrderList.Count == 0) { layer.LastFullscreenIndex = -1; return; } if (removedMeta.MetaInfo.FullScreen && layer.LastFullscreenIndex == removedIndex) { layer.LastFullscreenIndex = FindLastFullscreenIndex(layer.OrderList, removedIndex - 1); return; } if (removedIndex < layer.LastFullscreenIndex) { layer.LastFullscreenIndex--; } } private static void UpdateFullscreenIndexAfterMove(LayerData layer, UIMetadata meta, int fromIndex, int toIndex) { if (layer.LastFullscreenIndex == fromIndex) { layer.LastFullscreenIndex = toIndex; return; } if (!meta.MetaInfo.FullScreen) { return; } if (fromIndex < layer.LastFullscreenIndex) { layer.LastFullscreenIndex--; } layer.LastFullscreenIndex = Math.Max(layer.LastFullscreenIndex, toIndex); } } }