349 lines
13 KiB
C#
349 lines
13 KiB
C#
using System;
|
|
using AlicizaX.Editor;
|
|
using AlicizaX.UI.Runtime;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
namespace AlicizaX.UI.Editor
|
|
{
|
|
[CustomEditor(typeof(UIComponent))]
|
|
internal sealed class UIComponentInspector : GameFrameworkInspector
|
|
{
|
|
private const int CacheDebugInfoCapacity = 64;
|
|
private static readonly Color SectionColor = new Color(0.18f, 0.18f, 0.18f, 1f);
|
|
private static readonly Color OkColor = new Color(0.3f, 0.75f, 0.35f, 1f);
|
|
private static readonly Color WarningColor = new Color(1f, 0.72f, 0.18f, 1f);
|
|
private static readonly Color ErrorColor = new Color(1f, 0.32f, 0.28f, 1f);
|
|
private static readonly Color MutedColor = new Color(0.62f, 0.62f, 0.62f, 1f);
|
|
|
|
private readonly UIServiceDebugInfo _serviceInfo = new UIServiceDebugInfo();
|
|
private readonly UILayerDebugInfo _layerInfo = new UILayerDebugInfo();
|
|
private readonly UIWindowDebugInfo _windowInfo = new UIWindowDebugInfo();
|
|
private readonly UIWindowDebugInfo[] _cacheInfos = new UIWindowDebugInfo[CacheDebugInfoCapacity];
|
|
|
|
private SerializedProperty uiRoot;
|
|
private SerializedProperty _isOrthographic;
|
|
private bool _showRuntimeDebug = true;
|
|
private bool _showReferences;
|
|
private bool _showLayers = true;
|
|
private bool _showCache = true;
|
|
private bool _showEmptyLayers;
|
|
private Vector2 _runtimeScroll;
|
|
|
|
public override void OnInspectorGUI()
|
|
{
|
|
base.OnInspectorGUI();
|
|
serializedObject.Update();
|
|
|
|
EditorGUI.BeginDisabledGroup(EditorApplication.isPlayingOrWillChangePlaymode);
|
|
{
|
|
if (uiRoot.objectReferenceValue == null)
|
|
{
|
|
EditorGUILayout.HelpBox("uiroot can not be null!", MessageType.Error);
|
|
}
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
|
|
GameObject rootPrefab = (GameObject)EditorGUILayout.ObjectField("UI Root Prefab", uiRoot.objectReferenceValue, typeof(GameObject), false);
|
|
|
|
if (rootPrefab != uiRoot.objectReferenceValue)
|
|
{
|
|
uiRoot.objectReferenceValue = rootPrefab;
|
|
}
|
|
|
|
if (uiRoot.objectReferenceValue == null)
|
|
{
|
|
if (GUILayout.Button("Set Default"))
|
|
{
|
|
GameObject defaultPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(UIGlobalPath.UIPrefabPath);
|
|
uiRoot.objectReferenceValue = defaultPrefab;
|
|
}
|
|
}
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
EditorGUILayout.PropertyField(_isOrthographic);
|
|
}
|
|
EditorGUI.EndDisabledGroup();
|
|
serializedObject.ApplyModifiedProperties();
|
|
|
|
DrawRuntimeDebugInfo();
|
|
if (EditorApplication.isPlaying)
|
|
{
|
|
Repaint();
|
|
}
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
uiRoot = serializedObject.FindProperty("uiRoot");
|
|
_isOrthographic = serializedObject.FindProperty("_isOrthographic");
|
|
for (int i = 0; i < _cacheInfos.Length; i++)
|
|
{
|
|
_cacheInfos[i] = new UIWindowDebugInfo();
|
|
}
|
|
}
|
|
|
|
private void DrawRuntimeDebugInfo()
|
|
{
|
|
EditorGUILayout.Space();
|
|
_showRuntimeDebug = EditorGUILayout.Foldout(_showRuntimeDebug, "Runtime Debug", true, EditorStyles.foldoutHeader);
|
|
if (!_showRuntimeDebug)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!EditorApplication.isPlaying)
|
|
{
|
|
EditorGUILayout.HelpBox("Enter Play Mode to inspect runtime UI state.", MessageType.Info);
|
|
return;
|
|
}
|
|
|
|
if (!AppServices.TryGet<IUIService>(out IUIService uiService) || uiService is not IUIDebugService debugService)
|
|
{
|
|
EditorGUILayout.HelpBox("UI service is not initialized.", MessageType.Warning);
|
|
return;
|
|
}
|
|
|
|
debugService.FillServiceDebugInfo(_serviceInfo);
|
|
DrawServiceSummary();
|
|
DrawRuntimeOptions();
|
|
|
|
_runtimeScroll = EditorGUILayout.BeginScrollView(_runtimeScroll, GUILayout.MinHeight(260f), GUILayout.MaxHeight(720f));
|
|
DrawReferences();
|
|
DrawLayerDebugInfo(debugService);
|
|
DrawCacheDebugInfo(debugService);
|
|
EditorGUILayout.EndScrollView();
|
|
}
|
|
|
|
private void DrawRuntimeOptions()
|
|
{
|
|
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
|
_showLayers = GUILayout.Toggle(_showLayers, "Layers", EditorStyles.toolbarButton);
|
|
_showCache = GUILayout.Toggle(_showCache, "Cache", EditorStyles.toolbarButton);
|
|
_showReferences = GUILayout.Toggle(_showReferences, "References", EditorStyles.toolbarButton);
|
|
_showEmptyLayers = GUILayout.Toggle(_showEmptyLayers, "Empty Layers", EditorStyles.toolbarButton);
|
|
GUILayout.FlexibleSpace();
|
|
EditorGUILayout.EndHorizontal();
|
|
}
|
|
|
|
private void DrawServiceSummary()
|
|
{
|
|
DrawSectionBegin("Service Summary");
|
|
EditorGUILayout.BeginHorizontal();
|
|
DrawCounter("Initialized", _serviceInfo.Initialized ? "Yes" : "No", _serviceInfo.Initialized ? OkColor : ErrorColor);
|
|
DrawCounter("Mode", _serviceInfo.Orthographic ? "Orthographic" : "Perspective", Color.white);
|
|
DrawCounter("Layers", _serviceInfo.LayerCount.ToString(), Color.white);
|
|
DrawCounter("Open", _serviceInfo.OpenWindowCount.ToString(), _serviceInfo.OpenWindowCount > 0 ? OkColor : MutedColor);
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
DrawCounter("Cache", _serviceInfo.CacheWindowCount.ToString(), _serviceInfo.CacheWindowCount > 0 ? WarningColor : MutedColor);
|
|
DrawCounter("Update", _serviceInfo.UpdateWindowCount.ToString(), _serviceInfo.UpdateWindowCount > 0 ? OkColor : MutedColor);
|
|
DrawCounter("Block", _serviceInfo.BlockActive ? "Active" : "Inactive", _serviceInfo.BlockActive ? WarningColor : MutedColor);
|
|
DrawCounter("Block Timer", _serviceInfo.BlockTimerHandle.ToString(), _serviceInfo.BlockTimerHandle != 0UL ? WarningColor : MutedColor);
|
|
EditorGUILayout.EndHorizontal();
|
|
DrawSectionEnd();
|
|
}
|
|
|
|
private void DrawReferences()
|
|
{
|
|
if (!_showReferences)
|
|
{
|
|
return;
|
|
}
|
|
|
|
DrawSectionBegin("References");
|
|
EditorGUILayout.ObjectField("Root", _serviceInfo.Root, typeof(Transform), true);
|
|
EditorGUILayout.ObjectField("Canvas Root", _serviceInfo.CanvasRoot, typeof(Transform), true);
|
|
EditorGUILayout.ObjectField("Canvas", _serviceInfo.Canvas, typeof(Canvas), true);
|
|
EditorGUILayout.ObjectField("Camera", _serviceInfo.Camera, typeof(Camera), true);
|
|
DrawSectionEnd();
|
|
}
|
|
|
|
private void DrawLayerDebugInfo(IUIDebugService debugService)
|
|
{
|
|
if (!_showLayers)
|
|
{
|
|
return;
|
|
}
|
|
|
|
DrawSectionBegin("Open Windows");
|
|
bool hasWindow = false;
|
|
for (int layerIndex = 0; layerIndex < debugService.LayerCount; layerIndex++)
|
|
{
|
|
if (!debugService.FillLayerDebugInfo(layerIndex, _layerInfo))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (_layerInfo.WindowCount == 0 && !_showEmptyLayers)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
hasWindow |= _layerInfo.WindowCount > 0;
|
|
DrawLayerHeader(_layerInfo);
|
|
EditorGUI.indentLevel++;
|
|
for (int windowIndex = 0; windowIndex < _layerInfo.WindowCount; windowIndex++)
|
|
{
|
|
if (debugService.FillWindowDebugInfo(layerIndex, windowIndex, _windowInfo))
|
|
{
|
|
DrawWindowDebugInfo(_windowInfo, false);
|
|
}
|
|
}
|
|
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
|
|
if (!hasWindow && !_showEmptyLayers)
|
|
{
|
|
DrawEmptyLabel("No open UI windows.");
|
|
}
|
|
|
|
DrawSectionEnd();
|
|
}
|
|
|
|
private void DrawCacheDebugInfo(IUIDebugService debugService)
|
|
{
|
|
if (!_showCache)
|
|
{
|
|
return;
|
|
}
|
|
|
|
DrawSectionBegin("Cached Windows");
|
|
int count = debugService.FillCacheDebugInfo(_cacheInfos, _cacheInfos.Length);
|
|
if (count == 0)
|
|
{
|
|
DrawEmptyLabel("No cached UI windows.");
|
|
}
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
DrawWindowDebugInfo(_cacheInfos[i], true);
|
|
}
|
|
|
|
if (debugService.CacheWindowCount > _cacheInfos.Length)
|
|
{
|
|
EditorGUILayout.HelpBox("Cache list is truncated by inspector buffer capacity.", MessageType.Warning);
|
|
}
|
|
|
|
DrawSectionEnd();
|
|
}
|
|
|
|
private static void DrawLayerHeader(UILayerDebugInfo info)
|
|
{
|
|
Rect rect = EditorGUILayout.GetControlRect(false, EditorGUIUtility.singleLineHeight + 4f);
|
|
EditorGUI.DrawRect(rect, SectionColor);
|
|
rect.x += 6f;
|
|
rect.width -= 12f;
|
|
rect.y += 2f;
|
|
EditorGUI.LabelField(
|
|
rect,
|
|
info.Layer.ToString(),
|
|
"Windows " + info.WindowCount + " | Last Fullscreen " + info.LastFullscreenIndex,
|
|
EditorStyles.boldLabel);
|
|
}
|
|
|
|
private static void DrawWindowDebugInfo(UIWindowDebugInfo info, bool cached)
|
|
{
|
|
Rect rect = EditorGUILayout.GetControlRect(false, EditorGUIUtility.singleLineHeight + 4f);
|
|
Color color = GetWindowColor(info, cached);
|
|
EditorGUI.DrawRect(new Rect(rect.x, rect.y + 2f, 4f, rect.height - 4f), color);
|
|
rect.x += 8f;
|
|
rect.width -= 8f;
|
|
EditorGUI.ObjectField(rect, GetWindowTitle(info, cached), info.HolderTransform, typeof(Transform), true);
|
|
|
|
EditorGUI.indentLevel++;
|
|
EditorGUILayout.LabelField("State", GetStateLine(info));
|
|
EditorGUILayout.LabelField("Flags", GetFlagLine(info));
|
|
if (cached || info.CacheTime > 0f || info.CacheTimerHandle != 0UL)
|
|
{
|
|
EditorGUILayout.LabelField("Cache", GetCacheLine(info));
|
|
}
|
|
|
|
EditorGUILayout.LabelField("Holder", string.IsNullOrEmpty(info.HolderTypeName) ? "None" : info.HolderTypeName);
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
|
|
private static string GetWindowTitle(UIWindowDebugInfo info, bool cached)
|
|
{
|
|
string logicName = string.IsNullOrEmpty(info.LogicTypeName) ? "Unknown" : info.LogicTypeName;
|
|
string prefix = cached ? "[Cache] " : "";
|
|
return prefix + "#" + info.OrderIndex + " L" + info.LayerIndex + " " + logicName;
|
|
}
|
|
|
|
private static string GetStateLine(UIWindowDebugInfo info)
|
|
{
|
|
return info.State + " | Visible " + info.Visible + " | Depth " + info.Depth + " | FullScreen " + info.FullScreen;
|
|
}
|
|
|
|
private static string GetFlagLine(UIWindowDebugInfo info)
|
|
{
|
|
return "Update " + info.NeedUpdate + " | InCache " + info.InCache + " | ShowOp " + info.ShowInProgress + " | CloseOp " + info.CloseInProgress;
|
|
}
|
|
|
|
private static string GetCacheLine(UIWindowDebugInfo info)
|
|
{
|
|
return "Time " + info.CacheTime.ToString("F2") + " | Timer " + info.CacheTimerHandle;
|
|
}
|
|
|
|
private static Color GetWindowColor(UIWindowDebugInfo info, bool cached)
|
|
{
|
|
if (info.ShowInProgress || info.CloseInProgress)
|
|
{
|
|
return WarningColor;
|
|
}
|
|
|
|
if (cached || info.InCache)
|
|
{
|
|
return MutedColor;
|
|
}
|
|
|
|
if (!info.Visible)
|
|
{
|
|
return ErrorColor;
|
|
}
|
|
|
|
return info.FullScreen ? OkColor : Color.cyan;
|
|
}
|
|
|
|
private static void DrawCounter(string label, string value, Color valueColor)
|
|
{
|
|
EditorGUILayout.BeginVertical(GUI.skin.box, GUILayout.MinWidth(82f));
|
|
EditorGUILayout.LabelField(label, EditorStyles.miniLabel);
|
|
Color oldColor = GUI.color;
|
|
GUI.color = valueColor;
|
|
EditorGUILayout.LabelField(value, EditorStyles.boldLabel);
|
|
GUI.color = oldColor;
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
|
|
private static void DrawSectionBegin(string title)
|
|
{
|
|
EditorGUILayout.Space(4f);
|
|
Rect rect = EditorGUILayout.GetControlRect(false, EditorGUIUtility.singleLineHeight + 6f);
|
|
EditorGUI.DrawRect(rect, SectionColor);
|
|
rect.x += 6f;
|
|
rect.y += 3f;
|
|
rect.width -= 12f;
|
|
EditorGUI.LabelField(rect, title, EditorStyles.boldLabel);
|
|
EditorGUILayout.BeginVertical(GUI.skin.box);
|
|
}
|
|
|
|
private static void DrawSectionEnd()
|
|
{
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
|
|
private static void DrawEmptyLabel(string text)
|
|
{
|
|
Color oldColor = GUI.color;
|
|
GUI.color = MutedColor;
|
|
EditorGUILayout.LabelField(text, EditorStyles.miniLabel);
|
|
GUI.color = oldColor;
|
|
}
|
|
}
|
|
|
|
}
|