#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 Controllers = new List(); 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 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 results) { results.Clear(); UXController[] allControllers = Resources.FindObjectsOfTypeAll(); 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