450 lines
15 KiB
C#
450 lines
15 KiB
C#
using System;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Threading;
|
|
using AlicizaX;
|
|
using Cysharp.Threading.Tasks;
|
|
|
|
namespace AlicizaX.UI.Runtime
|
|
{
|
|
sealed class LayerData
|
|
{
|
|
public UIMetadata[] Items;
|
|
public int[] TypeIdToIndex;
|
|
public int Count;
|
|
public int LastFullscreenIndex;
|
|
|
|
public LayerData(int initialCapacity)
|
|
{
|
|
Items = new UIMetadata[initialCapacity];
|
|
TypeIdToIndex = new int[initialCapacity];
|
|
for (int i = 0; i < TypeIdToIndex.Length; i++)
|
|
{
|
|
TypeIdToIndex[i] = -1;
|
|
}
|
|
|
|
Count = 0;
|
|
LastFullscreenIndex = -1;
|
|
}
|
|
|
|
public void EnsureTypeCapacity(int typeId)
|
|
{
|
|
if ((uint)typeId < (uint)TypeIdToIndex.Length)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int oldLength = TypeIdToIndex.Length;
|
|
int newLength = oldLength;
|
|
while (newLength <= typeId)
|
|
{
|
|
newLength <<= 1;
|
|
}
|
|
|
|
Array.Resize(ref TypeIdToIndex, newLength);
|
|
for (int i = oldLength; i < newLength; i++)
|
|
{
|
|
TypeIdToIndex[i] = -1;
|
|
}
|
|
}
|
|
|
|
public void EnsureItemCapacity()
|
|
{
|
|
if (Count < Items.Length)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Array.Resize(ref Items, Items.Length << 1);
|
|
}
|
|
}
|
|
|
|
internal sealed partial class UIService
|
|
{
|
|
private readonly LayerData[] _openUI = new LayerData[(int)UILayer.All];
|
|
|
|
private async UniTask<UIBase> ShowUIImplAsync(UIMetadata metaInfo, params object[] userDatas)
|
|
{
|
|
CreateMetaUI(metaInfo);
|
|
if (!metaInfo.BeginShowOperation())
|
|
{
|
|
metaInfo.View?.RefreshParams(userDatas);
|
|
return metaInfo.View;
|
|
}
|
|
|
|
int operationVersion = metaInfo.OperationVersion;
|
|
await UIHolderFactory.CreateUIResourceAsync(metaInfo, UICacheLayer);
|
|
if (operationVersion != metaInfo.OperationVersion || metaInfo.View == null || metaInfo.State == UIState.Uninitialized || metaInfo.State == UIState.Destroyed)
|
|
{
|
|
metaInfo.EndShowOperation(operationVersion);
|
|
return null;
|
|
}
|
|
|
|
FinalizeShow(metaInfo, userDatas);
|
|
bool showResult = await UpdateVisualState(metaInfo, metaInfo.CancellationToken);
|
|
metaInfo.EndShowOperation(operationVersion);
|
|
return showResult ? metaInfo.View : null;
|
|
}
|
|
|
|
private UIBase ShowUIImplSync(UIMetadata metaInfo, params object[] userDatas)
|
|
{
|
|
CreateMetaUI(metaInfo);
|
|
if (!metaInfo.BeginShowOperation())
|
|
{
|
|
metaInfo.View?.RefreshParams(userDatas);
|
|
return metaInfo.View;
|
|
}
|
|
|
|
int operationVersion = metaInfo.OperationVersion;
|
|
UIHolderFactory.CreateUIResourceSync(metaInfo, UICacheLayer);
|
|
if (operationVersion != metaInfo.OperationVersion || metaInfo.View == null || metaInfo.State == UIState.Uninitialized || metaInfo.State == UIState.Destroyed)
|
|
{
|
|
metaInfo.EndShowOperation(operationVersion);
|
|
return null;
|
|
}
|
|
|
|
FinalizeShow(metaInfo, userDatas);
|
|
bool showResult = UpdateVisualStateSync(metaInfo);
|
|
metaInfo.EndShowOperation(operationVersion);
|
|
if (!showResult)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return metaInfo.View;
|
|
}
|
|
|
|
private async UniTask CloseUIImpl(UIMetadata meta, bool force)
|
|
{
|
|
if (meta.State == UIState.Uninitialized)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!meta.BeginCloseOperation())
|
|
{
|
|
return;
|
|
}
|
|
|
|
int operationVersion = meta.OperationVersion;
|
|
|
|
if (meta.State == UIState.CreatedUI)
|
|
{
|
|
await meta.DisposeAsync();
|
|
meta.EndCloseOperation(operationVersion);
|
|
return;
|
|
}
|
|
|
|
if (meta.State == UIState.Loaded || meta.State == UIState.Initialized)
|
|
{
|
|
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);
|
|
meta.EndCloseOperation(operationVersion);
|
|
return;
|
|
}
|
|
|
|
bool closeResult = await meta.View.InternalClose(meta.CancellationToken);
|
|
if (!closeResult || meta.State != UIState.Closed)
|
|
{
|
|
meta.EndCloseOperation(operationVersion);
|
|
return;
|
|
}
|
|
|
|
var closedPopResult = Pop(meta);
|
|
SortWindowVisible(meta.MetaInfo.UILayer, closedPopResult.previousFullscreenIndex);
|
|
SortWindowDepth(meta.MetaInfo.UILayer, closedPopResult.removedIndex >= 0 ? closedPopResult.removedIndex : 0);
|
|
CacheWindow(meta, force);
|
|
meta.EndCloseOperation(operationVersion);
|
|
}
|
|
|
|
|
|
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];
|
|
int typeId = meta.MetaInfo.TypeId;
|
|
layer.EnsureTypeCapacity(typeId);
|
|
if (layer.TypeIdToIndex[typeId] < 0)
|
|
{
|
|
layer.EnsureItemCapacity();
|
|
int index = layer.Count++;
|
|
layer.Items[index] = meta;
|
|
layer.TypeIdToIndex[typeId] = 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;
|
|
int typeId = meta.MetaInfo.TypeId;
|
|
if ((uint)typeId < (uint)layer.TypeIdToIndex.Length)
|
|
{
|
|
int index = layer.TypeIdToIndex[typeId];
|
|
if (index < 0)
|
|
{
|
|
return (-1, previousFullscreenIndex);
|
|
}
|
|
|
|
int lastIndex = layer.Count - 1;
|
|
for (int i = index; i < lastIndex; i++)
|
|
{
|
|
UIMetadata item = layer.Items[i + 1];
|
|
layer.Items[i] = item;
|
|
layer.TypeIdToIndex[item.MetaInfo.TypeId] = i;
|
|
}
|
|
|
|
layer.Items[lastIndex] = null;
|
|
layer.Count = lastIndex;
|
|
layer.TypeIdToIndex[typeId] = -1;
|
|
|
|
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.Count - 1;
|
|
int typeId = meta.MetaInfo.TypeId;
|
|
|
|
if ((uint)typeId >= (uint)layer.TypeIdToIndex.Length)
|
|
return;
|
|
|
|
int currentIdx = layer.TypeIdToIndex[typeId];
|
|
|
|
if (currentIdx != lastIdx && currentIdx >= 0)
|
|
{
|
|
for (int i = currentIdx; i < lastIdx; i++)
|
|
{
|
|
UIMetadata item = layer.Items[i + 1];
|
|
layer.Items[i] = item;
|
|
layer.TypeIdToIndex[item.MetaInfo.TypeId] = i;
|
|
}
|
|
|
|
layer.Items[lastIdx] = meta;
|
|
layer.TypeIdToIndex[typeId] = lastIdx;
|
|
UpdateFullscreenIndexAfterMove(layer, meta, currentIdx, lastIdx);
|
|
|
|
SortWindowDepth(meta.MetaInfo.UILayer, currentIdx);
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private async UniTask<bool> UpdateVisualState(UIMetadata meta, CancellationToken cancellationToken = default)
|
|
{
|
|
SortWindowVisible(meta.MetaInfo.UILayer);
|
|
SortWindowDepth(meta.MetaInfo.UILayer);
|
|
if (meta.State == UIState.Loaded)
|
|
{
|
|
if (!await meta.View.InternalInitlized(cancellationToken))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return await meta.View.InternalOpen(cancellationToken);
|
|
}
|
|
|
|
private bool UpdateVisualStateSync(UIMetadata meta)
|
|
{
|
|
SortWindowVisible(meta.MetaInfo.UILayer);
|
|
SortWindowDepth(meta.MetaInfo.UILayer);
|
|
if (meta.State == UIState.Loaded)
|
|
{
|
|
if (!meta.View.InternalInitlizedSync()) return false;
|
|
}
|
|
|
|
return meta.View.InternalOpenSync();
|
|
}
|
|
|
|
private void SortWindowVisible(int layer, int previousFullscreenIndex = int.MinValue)
|
|
{
|
|
var layerData = _openUI[layer];
|
|
int count = layerData.Count;
|
|
|
|
int fullscreenIdx = layerData.LastFullscreenIndex;
|
|
if (fullscreenIdx >= count || (fullscreenIdx >= 0 && !IsDisplayFullscreen(layerData.Items[fullscreenIdx])))
|
|
{
|
|
fullscreenIdx = FindLastFullscreenIndex(layerData, count - 1);
|
|
layerData.LastFullscreenIndex = fullscreenIdx;
|
|
}
|
|
|
|
int oldFullscreenIndex = previousFullscreenIndex == int.MinValue ? fullscreenIdx : previousFullscreenIndex;
|
|
if (oldFullscreenIndex == fullscreenIdx)
|
|
{
|
|
ApplyVisibilityRange(layerData, fullscreenIdx >= 0 ? fullscreenIdx : 0, count, fullscreenIdx);
|
|
return;
|
|
}
|
|
|
|
if (oldFullscreenIndex == -1 && fullscreenIdx == -1)
|
|
{
|
|
ApplyVisibilityRange(layerData, 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(layerData, start, endExclusive, fullscreenIdx);
|
|
}
|
|
|
|
private void SortWindowDepth(int layer, int startIndex = 0)
|
|
{
|
|
var layerData = _openUI[layer];
|
|
int baseDepth = layer * LAYER_DEEP;
|
|
|
|
for (int i = startIndex; i < layerData.Count; i++)
|
|
{
|
|
int newDepth = baseDepth + i * WINDOW_DEEP;
|
|
|
|
if (layerData.Items[i].View.Depth != newDepth)
|
|
{
|
|
layerData.Items[i].View.Depth = newDepth;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static bool IsDisplayFullscreen(UIMetadata meta)
|
|
{
|
|
return meta.MetaInfo.FullScreen && UIStateMachine.IsDisplayActive(meta.State);
|
|
}
|
|
|
|
private static int FindLastFullscreenIndex(LayerData layer, int startIndex)
|
|
{
|
|
for (int i = Math.Min(startIndex, layer.Count - 1); i >= 0; i--)
|
|
{
|
|
if (IsDisplayFullscreen(layer.Items[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
private static void ApplyVisibilityRange(LayerData layer, int startInclusive, int endExclusive, int fullscreenIdx)
|
|
{
|
|
if (startInclusive < 0)
|
|
{
|
|
startInclusive = 0;
|
|
}
|
|
|
|
if (endExclusive > layer.Count)
|
|
{
|
|
endExclusive = layer.Count;
|
|
}
|
|
|
|
bool showAll = fullscreenIdx < 0;
|
|
for (int i = startInclusive; i < endExclusive; i++)
|
|
{
|
|
layer.Items[i].View.Visible = showAll || i >= fullscreenIdx;
|
|
}
|
|
}
|
|
|
|
private static void UpdateFullscreenIndexAfterRemove(LayerData layer, UIMetadata removedMeta, int removedIndex)
|
|
{
|
|
if (layer.Count == 0)
|
|
{
|
|
layer.LastFullscreenIndex = -1;
|
|
return;
|
|
}
|
|
|
|
if (removedMeta.MetaInfo.FullScreen && layer.LastFullscreenIndex == removedIndex)
|
|
{
|
|
layer.LastFullscreenIndex = FindLastFullscreenIndex(layer, 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);
|
|
}
|
|
}
|
|
}
|