237 lines
7.9 KiB
C#
237 lines
7.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.CompilerServices;
|
|
using AlicizaX;
|
|
using Cysharp.Threading.Tasks;
|
|
|
|
namespace AlicizaX.UI.Runtime
|
|
{
|
|
readonly struct LayerData
|
|
{
|
|
public readonly List<UIMetadata> OrderList; // 维护插入顺序
|
|
public readonly HashSet<RuntimeTypeHandle> HandleSet; // O(1)存在性检查
|
|
public readonly Dictionary<RuntimeTypeHandle, int> IndexMap; // O(1)索引查找
|
|
|
|
public LayerData(int initialCapacity)
|
|
{
|
|
OrderList = new List<UIMetadata>(initialCapacity);
|
|
HandleSet = new HashSet<RuntimeTypeHandle>();
|
|
IndexMap = new Dictionary<RuntimeTypeHandle, int>(initialCapacity);
|
|
}
|
|
}
|
|
|
|
internal sealed partial class UIModule
|
|
{
|
|
private readonly LayerData[] _openUI = new LayerData[(int)UILayer.All];
|
|
|
|
private async UniTask<UIBase> ShowUIImplAsync(UIMetadata metaInfo, params object[] userDatas)
|
|
{
|
|
CreateMetaUI(metaInfo);
|
|
await UIHolderFactory.CreateUIResourceAsync(metaInfo, UICacheLayer);
|
|
FinalizeShow(metaInfo, userDatas);
|
|
UpdateVisualState(metaInfo).Forget();
|
|
return metaInfo.View;
|
|
}
|
|
|
|
private UIBase ShowUIImplSync(UIMetadata metaInfo, params object[] userDatas)
|
|
{
|
|
CreateMetaUI(metaInfo);
|
|
UIHolderFactory.CreateUIResourceSync(metaInfo, UICacheLayer);
|
|
FinalizeShow(metaInfo, userDatas);
|
|
UpdateVisualState(metaInfo).Forget();
|
|
return metaInfo.View;
|
|
}
|
|
|
|
private async UniTask CloseUIImpl(UIMetadata meta, bool force)
|
|
{
|
|
if (meta.State == UIState.Uninitialized || meta.State == UIState.CreatedUI)
|
|
{
|
|
return;
|
|
}
|
|
|
|
await meta.View.InternalClose();
|
|
Pop(meta);
|
|
SortWindowVisible(meta.MetaInfo.UILayer);
|
|
SortWindowDepth(meta.MetaInfo.UILayer);
|
|
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.Opened:
|
|
MoveToTop(meta);
|
|
break;
|
|
}
|
|
}
|
|
|
|
meta.View.RefreshParams(userDatas);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private void Push(UIMetadata meta)
|
|
{
|
|
ref var layer = ref _openUI[meta.MetaInfo.UILayer];
|
|
if (layer.HandleSet.Add(meta.MetaInfo.RuntimeTypeHandle))
|
|
{
|
|
int index = layer.OrderList.Count;
|
|
layer.OrderList.Add(meta);
|
|
layer.IndexMap[meta.MetaInfo.RuntimeTypeHandle] = index;
|
|
UpdateLayerParent(meta);
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private void Pop(UIMetadata meta)
|
|
{
|
|
ref var layer = ref _openUI[meta.MetaInfo.UILayer];
|
|
if (layer.HandleSet.Remove(meta.MetaInfo.RuntimeTypeHandle))
|
|
{
|
|
if (layer.IndexMap.TryGetValue(meta.MetaInfo.RuntimeTypeHandle, out int index))
|
|
{
|
|
layer.OrderList.RemoveAt(index);
|
|
layer.IndexMap.Remove(meta.MetaInfo.RuntimeTypeHandle);
|
|
|
|
// Update indices for all elements after the removed one
|
|
for (int i = index; i < layer.OrderList.Count; i++)
|
|
{
|
|
var m = layer.OrderList[i];
|
|
layer.IndexMap[m.MetaInfo.RuntimeTypeHandle] = i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[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)
|
|
{
|
|
ref var layer = ref _openUI[meta.MetaInfo.UILayer];
|
|
int lastIdx = layer.OrderList.Count - 1;
|
|
|
|
// O(1) lookup instead of O(n) IndexOf
|
|
if (!layer.IndexMap.TryGetValue(meta.MetaInfo.RuntimeTypeHandle, out int currentIdx))
|
|
return;
|
|
|
|
if (currentIdx != lastIdx && currentIdx >= 0)
|
|
{
|
|
layer.OrderList.RemoveAt(currentIdx);
|
|
layer.OrderList.Add(meta);
|
|
|
|
// Update indices for shifted elements
|
|
for (int i = currentIdx; i < lastIdx; i++)
|
|
{
|
|
var m = layer.OrderList[i];
|
|
layer.IndexMap[m.MetaInfo.RuntimeTypeHandle] = i;
|
|
}
|
|
|
|
// Update moved element's index
|
|
layer.IndexMap[meta.MetaInfo.RuntimeTypeHandle] = lastIdx;
|
|
|
|
// Only update depth for affected windows (from currentIdx onwards)
|
|
SortWindowDepth(meta.MetaInfo.UILayer, currentIdx);
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private async UniTask UpdateVisualState(UIMetadata meta)
|
|
{
|
|
SortWindowVisible(meta.MetaInfo.UILayer);
|
|
SortWindowDepth(meta.MetaInfo.UILayer);
|
|
if (meta.State == UIState.Loaded)
|
|
{
|
|
await meta.View.InternalInitlized();
|
|
}
|
|
|
|
await meta.View.InternalOpen();
|
|
}
|
|
|
|
private void SortWindowVisible(int layer)
|
|
{
|
|
var list = _openUI[layer].OrderList;
|
|
int count = list.Count;
|
|
|
|
// Find topmost fullscreen window (early exit optimization)
|
|
int fullscreenIdx = -1;
|
|
for (int i = count - 1; i >= 0; i--)
|
|
{
|
|
var meta = list[i];
|
|
if (meta.MetaInfo.FullScreen && meta.State == UIState.Opened)
|
|
{
|
|
fullscreenIdx = i;
|
|
break; // Early exit - found topmost fullscreen
|
|
}
|
|
}
|
|
|
|
// Set visibility based on fullscreen index
|
|
if (fullscreenIdx == -1)
|
|
{
|
|
// No fullscreen window, all visible
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
list[i].View.Visible = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Hide windows below fullscreen, show from fullscreen onwards
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
list[i].View.Visible = (i >= fullscreenIdx);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SortWindowDepth(int layer, int startIndex = 0)
|
|
{
|
|
var list = _openUI[layer].OrderList;
|
|
int baseDepth = layer * LAYER_DEEP;
|
|
|
|
// Only update from startIndex onwards (optimization for partial updates)
|
|
for (int i = startIndex; i < list.Count; i++)
|
|
{
|
|
int newDepth = baseDepth + i * WINDOW_DEEP;
|
|
|
|
// Only set if changed to avoid unnecessary Canvas updates
|
|
if (list[i].View.Depth != newDepth)
|
|
{
|
|
list[i].View.Depth = newDepth;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|