434 lines
14 KiB
C#
434 lines
14 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using AlicizaX.UXTool;
|
|
using UnityEditor;
|
|
using UnityEditor.Experimental.SceneManagement;
|
|
using UnityEditor.SceneManagement;
|
|
using UnityEngine;
|
|
using UnityEngine.UIElements;
|
|
using UnityEditor.Callbacks;
|
|
using AlicizaX.UI;
|
|
using AlicizaX.UI.Runtime;
|
|
|
|
public static class UXControllerSceneOverlayManager
|
|
{
|
|
private static readonly Dictionary<int, UXControllerOverlayVE> s_map = new Dictionary<int, UXControllerOverlayVE>();
|
|
|
|
[InitializeOnLoadMethod]
|
|
public static void Initialize()
|
|
{
|
|
UXDesinUtil.OnEnterDesignMode += RegisterEvents;
|
|
UXDesinUtil.OnExitDesignMode += UnRegisterEvents;
|
|
|
|
if (UXDesinUtil.InDesign)
|
|
{
|
|
RegisterEvents();
|
|
}
|
|
else
|
|
{
|
|
UnRegisterEvents();
|
|
}
|
|
}
|
|
|
|
private static void RegisterEvents()
|
|
{
|
|
SceneView.duringSceneGui += OnSceneGui;
|
|
EditorApplication.update += EditorUpdate;
|
|
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
|
}
|
|
|
|
private static void UnRegisterEvents()
|
|
{
|
|
SceneView.duringSceneGui -= OnSceneGui;
|
|
EditorApplication.update -= EditorUpdate;
|
|
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
|
|
|
|
RemoveAllOverlays();
|
|
}
|
|
|
|
private static void RemoveAllOverlays()
|
|
{
|
|
var views = SceneView.sceneViews;
|
|
foreach (var obj in views)
|
|
{
|
|
var sv = obj as SceneView;
|
|
if (sv == null) continue;
|
|
try
|
|
{
|
|
var root = sv.rootVisualElement;
|
|
if (root == null) continue;
|
|
var existing = root.Q<VisualElement>("ux-controller-ve");
|
|
if (existing != null)
|
|
{
|
|
root.Remove(existing);
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
}
|
|
}
|
|
|
|
// 清理字典
|
|
s_map.Clear();
|
|
}
|
|
|
|
private static void OnPlayModeStateChanged(PlayModeStateChange state)
|
|
{
|
|
SceneView.RepaintAll();
|
|
}
|
|
|
|
private static void OnSceneGui(SceneView sv)
|
|
{
|
|
if (sv == null) return;
|
|
if (PrefabStageUtils.InEmptyStage) return;
|
|
// ensure overlay exists for this SceneView
|
|
var root = sv.rootVisualElement;
|
|
if (root == null) return;
|
|
|
|
int id = sv.GetInstanceID();
|
|
if (!s_map.ContainsKey(id))
|
|
{
|
|
// avoid duplicate q by name
|
|
var existing = root.Q<VisualElement>("ux-controller-ve");
|
|
if (existing != null)
|
|
{
|
|
// if already present (from another vm), keep it and track
|
|
var ve = existing as UXControllerOverlayVE;
|
|
if (ve != null) s_map[id] = ve;
|
|
}
|
|
else
|
|
{
|
|
var ve = new UXControllerOverlayVE();
|
|
ve.name = "ux-controller-ve";
|
|
// place at top so it overlays scene view content
|
|
ve.style.position = Position.Absolute;
|
|
ve.style.left = 0;
|
|
ve.style.top = 0;
|
|
ve.style.width = Length.Percent(100);
|
|
// z-index: bring to front
|
|
// ve.style.unityZIndex = 1000;
|
|
root.Add(ve);
|
|
s_map[id] = ve;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void EditorUpdate()
|
|
{
|
|
// Refresh all overlays (they check for changes internally and early out)
|
|
foreach (var kv in s_map)
|
|
{
|
|
var ve = kv.Value;
|
|
if (ve != null) ve.EditorUpdate();
|
|
}
|
|
}
|
|
}
|
|
|
|
public class UXControllerOverlayVE : VisualElement
|
|
{
|
|
private VisualElement _root;
|
|
private VisualElement _controllerList;
|
|
private Button _addBtn;
|
|
private PrefabStage _currentPrefabStage;
|
|
private UXController _currentUX;
|
|
private int _lastControllersHash = 0;
|
|
|
|
public UXControllerOverlayVE()
|
|
{
|
|
style.flexDirection = FlexDirection.Column;
|
|
style.paddingLeft = 6;
|
|
style.paddingTop = 6;
|
|
style.paddingRight = 6;
|
|
style.paddingBottom = 6;
|
|
style.width = Length.Percent(100);
|
|
style.unityBackgroundImageTintColor = Color.white;
|
|
style.height = Length.Percent(5);
|
|
|
|
|
|
style.backgroundColor = new StyleColor(new Color(0.12f, 0.12f, 0.12f, 0.95f));
|
|
style.borderTopWidth = 1;
|
|
style.borderBottomWidth = 1;
|
|
style.borderLeftWidth = 1;
|
|
style.borderRightWidth = 1;
|
|
style.borderTopColor = new StyleColor(new Color(0, 0, 0, 0.6f));
|
|
style.borderBottomColor = new StyleColor(new Color(0, 0, 0, 0.6f));
|
|
style.borderLeftColor = new StyleColor(new Color(0, 0, 0, 0.6f));
|
|
style.borderRightColor = new StyleColor(new Color(0, 0, 0, 0.6f));
|
|
style.paddingBottom = 8;
|
|
style.paddingTop = 6;
|
|
style.paddingLeft = 6;
|
|
style.paddingRight = 6;
|
|
|
|
_addBtn = new Button(() =>
|
|
{
|
|
if (_currentUX != null)
|
|
{
|
|
UXControllerAddWindow.ShowWindow(_currentUX);
|
|
}
|
|
else
|
|
{
|
|
var prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
|
|
if (prefabStage != null && prefabStage.prefabContentsRoot != null)
|
|
{
|
|
Undo.AddComponent<UXController>(prefabStage.prefabContentsRoot);
|
|
EditorUtility.SetDirty(prefabStage.prefabContentsRoot);
|
|
}
|
|
}
|
|
|
|
Refresh();
|
|
});
|
|
|
|
_addBtn.style.width = 22;
|
|
_addBtn.style.height = 22;
|
|
_addBtn.style.backgroundColor = new StyleColor(new Color(0.26f, 0.26f, 0.26f));
|
|
_addBtn.style.marginRight = 0;
|
|
_addBtn.style.borderBottomLeftRadius = 4;
|
|
_addBtn.style.borderBottomRightRadius = 4;
|
|
_addBtn.style.borderTopLeftRadius = 4;
|
|
_addBtn.style.borderTopRightRadius = 4;
|
|
_addBtn.style.alignSelf = Align.Center;
|
|
_addBtn.style.justifyContent = Justify.Center;
|
|
_addBtn.tooltip = "添加新的控制器";
|
|
|
|
var cLabel = new Label("+");
|
|
cLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
|
|
cLabel.style.unityTextAlign = TextAnchor.MiddleCenter;
|
|
cLabel.style.color = new StyleColor(Color.white);
|
|
cLabel.style.fontSize = 12;
|
|
cLabel.style.flexGrow = 0;
|
|
_addBtn.Add(cLabel);
|
|
|
|
|
|
var scroll = new ScrollView(ScrollViewMode.Horizontal);
|
|
scroll.style.flexDirection = FlexDirection.Row;
|
|
scroll.style.paddingLeft = 0;
|
|
scroll.style.paddingRight = 2;
|
|
scroll.style.paddingBottom = 0;
|
|
scroll.style.paddingTop = 0;
|
|
scroll.horizontalScrollerVisibility = ScrollerVisibility.Hidden;
|
|
scroll.verticalScrollerVisibility = ScrollerVisibility.Hidden;
|
|
|
|
_controllerList = new VisualElement();
|
|
_controllerList.style.flexDirection = FlexDirection.Row;
|
|
_controllerList.style.flexWrap = Wrap.Wrap;
|
|
_controllerList.style.alignItems = Align.FlexStart;
|
|
|
|
scroll.Add(_controllerList);
|
|
scroll.Add(_addBtn);
|
|
this.Add(scroll);
|
|
|
|
Refresh();
|
|
}
|
|
|
|
public void EditorUpdate()
|
|
{
|
|
// called by manager on EditorApplication.update
|
|
var prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
|
|
if (prefabStage != _currentPrefabStage)
|
|
{
|
|
_currentPrefabStage = prefabStage;
|
|
Refresh();
|
|
return;
|
|
}
|
|
|
|
if (_currentUX != null)
|
|
{
|
|
int h = ComputeControllersHash(_currentUX);
|
|
if (h != _lastControllersHash)
|
|
{
|
|
_lastControllersHash = h;
|
|
Refresh();
|
|
}
|
|
}
|
|
}
|
|
|
|
private int ComputeControllersHash(UXController ux)
|
|
{
|
|
if (ux == null) return 0;
|
|
unchecked
|
|
{
|
|
int hash = 17;
|
|
var list = ux.Controllers;
|
|
if (list != null)
|
|
{
|
|
foreach (var cd in list)
|
|
{
|
|
hash = hash * 23 + (cd?.Name?.GetHashCode() ?? 0);
|
|
hash = hash * 23 + (cd?.Length.GetHashCode() ?? 0);
|
|
hash = hash * 23 + (cd?.CurrentIndex.GetHashCode() ?? 0);
|
|
}
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
}
|
|
|
|
public void Refresh()
|
|
{
|
|
_controllerList.Clear();
|
|
|
|
var prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
|
|
_currentPrefabStage = prefabStage;
|
|
_currentUX = null;
|
|
if (prefabStage != null && prefabStage.prefabContentsRoot != null)
|
|
{
|
|
_currentUX = prefabStage.prefabContentsRoot.GetComponent<UXController>();
|
|
}
|
|
|
|
if (PrefabStageUtils.InEmptyStage)
|
|
{
|
|
_addBtn.style.display = DisplayStyle.None;
|
|
return;
|
|
}
|
|
|
|
_addBtn.style.display = DisplayStyle.Flex;
|
|
if (_currentUX == null)
|
|
{
|
|
var empty = new Label("当前 Prefab 未包含 UXController。点击右侧按钮可添加。");
|
|
empty.style.unityFontStyleAndWeight = FontStyle.Italic;
|
|
empty.style.color = new StyleColor(new Color(0.75f, 0.75f, 0.75f));
|
|
empty.style.marginLeft = 6;
|
|
empty.style.marginTop = 6;
|
|
_controllerList.Add(empty);
|
|
_lastControllersHash = 0;
|
|
return;
|
|
}
|
|
|
|
var controllers = _currentUX.Controllers;
|
|
if (controllers == null) return;
|
|
|
|
for (int i = 0; i < controllers.Count; i++)
|
|
{
|
|
var cd = controllers[i];
|
|
var card = CreateControllerCard(cd, i);
|
|
_controllerList.Add(card);
|
|
}
|
|
|
|
_lastControllersHash = ComputeControllersHash(_currentUX);
|
|
}
|
|
|
|
private VisualElement CreateControllerCard(UXController.ControllerData cd, int index)
|
|
{
|
|
var card = new VisualElement();
|
|
card.style.flexDirection = FlexDirection.Row;
|
|
card.style.alignItems = Align.Center;
|
|
card.style.paddingLeft = 6;
|
|
card.style.paddingRight = 6;
|
|
card.style.paddingTop = 4;
|
|
card.style.paddingBottom = 4;
|
|
card.style.backgroundColor = new StyleColor(new Color(0.13f, 0.13f, 0.13f));
|
|
card.style.borderTopLeftRadius = 6;
|
|
card.style.borderBottomLeftRadius = 6;
|
|
card.style.borderTopRightRadius = 6;
|
|
card.style.borderBottomRightRadius = 6;
|
|
card.style.minHeight = 28;
|
|
card.style.marginRight = 4;
|
|
card.style.marginBottom = 6;
|
|
|
|
// icon (C with underline)
|
|
var cBox = new VisualElement();
|
|
cBox.style.width = 22;
|
|
cBox.style.height = 22;
|
|
cBox.style.backgroundColor = new StyleColor(new Color(0.26f, 0.26f, 0.26f));
|
|
cBox.style.marginRight = 0;
|
|
cBox.style.borderBottomLeftRadius = 4;
|
|
cBox.style.borderBottomRightRadius = 4;
|
|
cBox.style.borderTopLeftRadius = 4;
|
|
cBox.style.borderTopRightRadius = 4;
|
|
cBox.style.alignSelf = Align.Center;
|
|
cBox.style.justifyContent = Justify.Center;
|
|
|
|
var cLabel = new Label("C");
|
|
cLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
|
|
cLabel.style.unityTextAlign = TextAnchor.MiddleCenter;
|
|
cLabel.style.color = new StyleColor(Color.white);
|
|
cLabel.style.fontSize = 12;
|
|
cLabel.style.flexGrow = 0;
|
|
cBox.Add(cLabel);
|
|
|
|
var underline = new VisualElement();
|
|
underline.style.position = Position.Absolute;
|
|
underline.style.height = 2;
|
|
underline.style.width = 14;
|
|
underline.style.left = 4;
|
|
underline.style.bottom = 3;
|
|
underline.style.backgroundColor = new StyleColor(Color.white);
|
|
|
|
cBox.Add(underline);
|
|
|
|
card.Add(cBox);
|
|
|
|
|
|
var nameBtn = new Button(() => { UXControllerEditWindow.ShowWindow(_currentUX, index, cd.Name, cd.Length); });
|
|
nameBtn.text = cd.Name;
|
|
nameBtn.style.unityTextAlign = TextAnchor.MiddleLeft;
|
|
nameBtn.style.width = 120;
|
|
nameBtn.style.height = 22;
|
|
nameBtn.style.marginRight = 6;
|
|
|
|
card.Add(nameBtn);
|
|
|
|
|
|
var indicesContainer = new VisualElement();
|
|
indicesContainer.style.flexDirection = FlexDirection.Row;
|
|
indicesContainer.style.alignItems = Align.Center;
|
|
|
|
int length = Math.Max(1, cd.Length);
|
|
for (int idx = 0; idx < length; idx++)
|
|
{
|
|
int capturedIdx = idx;
|
|
bool isSelected = (cd.CurrentIndex == idx);
|
|
|
|
var idxBtn = new Button(() =>
|
|
{
|
|
SetControllerIndexViaReflection(_currentUX, index, capturedIdx);
|
|
Refresh(); // force update visual state
|
|
});
|
|
idxBtn.text = idx.ToString();
|
|
idxBtn.style.width = 22;
|
|
idxBtn.style.height = 18;
|
|
idxBtn.style.unityTextAlign = TextAnchor.MiddleCenter;
|
|
idxBtn.style.paddingLeft = 0;
|
|
idxBtn.style.paddingRight = 0;
|
|
idxBtn.style.paddingTop = 0;
|
|
idxBtn.style.paddingBottom = 0;
|
|
idxBtn.style.fontSize = 11;
|
|
idxBtn.style.borderTopLeftRadius = 3;
|
|
idxBtn.style.borderTopRightRadius = 3;
|
|
idxBtn.style.borderBottomLeftRadius = 3;
|
|
idxBtn.style.borderBottomRightRadius = 3;
|
|
idxBtn.style.marginRight = 4;
|
|
|
|
if (isSelected)
|
|
{
|
|
idxBtn.style.backgroundColor = new StyleColor(new Color(0.9f, 0.55f, 0.1f));
|
|
idxBtn.style.color = new StyleColor(Color.white);
|
|
}
|
|
else
|
|
{
|
|
idxBtn.style.backgroundColor = new StyleColor(new Color(0.18f, 0.18f, 0.18f));
|
|
idxBtn.style.color = new StyleColor(Color.white);
|
|
}
|
|
|
|
indicesContainer.Add(idxBtn);
|
|
}
|
|
|
|
card.Add(indicesContainer);
|
|
|
|
return card;
|
|
}
|
|
|
|
private static void SetControllerIndexViaReflection(UXController ux, int controllerIndex, int selectedIndex)
|
|
{
|
|
if (ux == null) return;
|
|
var t = ux.GetType();
|
|
var mi = t.GetMethod("SetControllerIndex", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
|
if (mi != null)
|
|
{
|
|
mi.Invoke(ux, new object[] { controllerIndex, selectedIndex });
|
|
EditorUtility.SetDirty(ux);
|
|
}
|
|
}
|
|
}
|