com.alicizax.unity.ui.exten.../Editor/UX/Controller/UXControllerSceneOverlayWindow.cs
陈思海 526341579a 优化UIExtension扩展系统
优化UXBinding编辑器
优化UXBinding性能
优化UXBinding Bug
优化Hotkey注册器性能
优化UXNavigation导航性能
2026-04-28 20:52:06 +08:00

269 lines
8.9 KiB
C#

#if UNITY_EDITOR
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace UnityEngine.UI
{
public sealed class UXControllerSceneOverlayWindow : EditorWindow
{
private const string OverlayEnabledKey = "AlicizaX.UI.UXControllerSceneOverlay.Enabled";
private const string MenuPath = "Window/UX/Controller Scene Overlay";
private const float OverlayWidth = 340f;
private const float OverlayMargin = 10f;
private static bool s_overlayEnabled;
private static Vector2 s_scrollPosition;
private static bool s_registered;
private static readonly List<UXController> Controllers = new List<UXController>();
private static GUIStyle s_indexOnStyle;
private static GUIStyle s_indexOffStyle;
[MenuItem(MenuPath)]
public static void ToggleOverlay()
{
s_overlayEnabled = !s_overlayEnabled;
EditorPrefs.SetBool(OverlayEnabledKey, s_overlayEnabled);
Menu.SetChecked(MenuPath, s_overlayEnabled);
EnsureRegistered();
SceneView.RepaintAll();
}
[MenuItem(MenuPath, true)]
private static bool ToggleOverlayValidate()
{
Menu.SetChecked(MenuPath, s_overlayEnabled);
return true;
}
[InitializeOnLoadMethod]
private static void Initialize()
{
s_overlayEnabled = EditorPrefs.GetBool(OverlayEnabledKey, false);
Menu.SetChecked(MenuPath, s_overlayEnabled);
EnsureRegistered();
}
private static void EnsureRegistered()
{
if (s_registered)
{
return;
}
SceneView.duringSceneGui += OnSceneGui;
s_registered = true;
}
private static void OnSceneGui(SceneView sceneView)
{
if (!s_overlayEnabled)
{
return;
}
GetSceneControllers(Controllers);
Handles.BeginGUI();
float height = Mathf.Min(Mathf.Max(180f, sceneView.position.height - OverlayMargin * 2f), 520f);
Rect area = new Rect(
sceneView.position.width - OverlayWidth - OverlayMargin,
OverlayMargin,
OverlayWidth,
height);
GUILayout.BeginArea(area, GUI.skin.window);
DrawOverlayContent(Controllers);
GUILayout.EndArea();
Handles.EndGUI();
}
private static void DrawOverlayContent(List<UXController> controllers)
{
EnsureStyles();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("UX Controller Preview", EditorStyles.boldLabel);
EditorGUILayout.LabelField(controllers.Count.ToString(), EditorStyles.miniBoldLabel, GUILayout.Width(24f));
GUILayout.FlexibleSpace();
if (GUILayout.Button("X", EditorStyles.miniButton, GUILayout.Width(22f)))
{
s_overlayEnabled = false;
EditorPrefs.SetBool(OverlayEnabledKey, s_overlayEnabled);
Menu.SetChecked(MenuPath, false);
SceneView.RepaintAll();
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space(2f);
if (controllers.Count == 0)
{
EditorGUILayout.HelpBox("No UXController in loaded scenes.", MessageType.Info);
return;
}
s_scrollPosition = EditorGUILayout.BeginScrollView(s_scrollPosition);
for (int i = 0; i < controllers.Count; i++)
{
DrawControllerCard(controllers[i], i);
EditorGUILayout.Space(4f);
}
EditorGUILayout.EndScrollView();
}
private static void DrawControllerCard(UXController controller, int index)
{
if (controller == null)
{
return;
}
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(controller.gameObject.name, EditorStyles.boldLabel);
EditorGUILayout.LabelField($"C:{controller.ControllerCount} B:{controller.Bindings.Count}", EditorStyles.miniLabel, GUILayout.Width(72f));
GUILayout.FlexibleSpace();
if (GUILayout.Button("Ping", EditorStyles.miniButtonLeft, GUILayout.Width(42f)))
{
Selection.activeGameObject = controller.gameObject;
EditorGUIUtility.PingObject(controller.gameObject);
}
if (GUILayout.Button("Reset", EditorStyles.miniButtonRight, GUILayout.Width(44f)))
{
controller.ResetAllControllers();
EditorUtility.SetDirty(controller);
SceneView.RepaintAll();
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.LabelField(GetHierarchyPath(controller.transform), EditorStyles.miniLabel);
for (int controllerIndex = 0; controllerIndex < controller.ControllerCount; controllerIndex++)
{
UXController.ControllerDefinition definition = controller.GetControllerAt(controllerIndex);
if (definition == null)
{
continue;
}
DrawDefinitionPreview(controller, definition);
}
EditorGUILayout.EndVertical();
}
private static void DrawDefinitionPreview(UXController controller, UXController.ControllerDefinition definition)
{
int currentIndex = Mathf.Max(0, definition.SelectedIndex);
int length = Mathf.Max(1, definition.Length);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(definition.Name, EditorStyles.miniBoldLabel, GUILayout.Width(96f));
for (int i = 0; i < length; i++)
{
bool selected = i == currentIndex;
GUIStyle style = selected ? s_indexOnStyle : s_indexOffStyle;
if (GUILayout.Toggle(selected, i.ToString(), style, GUILayout.Width(26f)) != selected)
{
controller.SetControllerIndex(definition.Id, i);
EditorUtility.SetDirty(controller);
SceneView.RepaintAll();
}
}
EditorGUILayout.EndHorizontal();
if (!string.IsNullOrWhiteSpace(definition.Description))
{
EditorGUILayout.LabelField(definition.Description, EditorStyles.miniLabel);
}
}
private static void GetSceneControllers(List<UXController> results)
{
results.Clear();
UXController[] allControllers = Resources.FindObjectsOfTypeAll<UXController>();
for (int i = 0; i < allControllers.Length; i++)
{
UXController controller = allControllers[i];
if (controller == null)
{
continue;
}
if (EditorUtility.IsPersistent(controller))
{
continue;
}
if (!controller.gameObject.scene.IsValid() || !controller.gameObject.scene.isLoaded)
{
continue;
}
if ((controller.hideFlags & HideFlags.HideInHierarchy) != 0)
{
continue;
}
results.Add(controller);
}
results.Sort(CompareControllers);
}
private static int CompareControllers(UXController left, UXController right)
{
return string.CompareOrdinal(GetHierarchyPath(left.transform), GetHierarchyPath(right.transform));
}
private static void EnsureStyles()
{
if (s_indexOnStyle != null)
{
return;
}
s_indexOnStyle = new GUIStyle(EditorStyles.miniButton)
{
fontStyle = FontStyle.Bold,
fixedHeight = 18f,
margin = new RectOffset(1, 1, 1, 1),
padding = new RectOffset(2, 2, 1, 1)
};
s_indexOffStyle = new GUIStyle(EditorStyles.miniButton)
{
fixedHeight = 18f,
margin = new RectOffset(1, 1, 1, 1),
padding = new RectOffset(2, 2, 1, 1)
};
}
private static string GetHierarchyPath(Transform target)
{
if (target == null)
{
return string.Empty;
}
string path = target.name;
Transform current = target.parent;
while (current != null)
{
path = $"{current.name}/{path}";
current = current.parent;
}
return path;
}
}
}
#endif