using System.Collections.Generic; using UnityEditorInternal; using UnityEngine; using UnityEngine.UI; using UnityEditor; namespace UnityEditor.UI { [CustomEditor(typeof(UXGroup))] public class UXGroupInspector : Editor { private SerializedProperty m_Toggles; private SerializedProperty m_AllowSwitchOff; private SerializedProperty m_DefaultToggle; private UXGroup _target; private ReorderableList _reorderableList; private void OnEnable() { _target = (UXGroup)target; m_Toggles = serializedObject.FindProperty("m_Toggles"); m_AllowSwitchOff = serializedObject.FindProperty("m_AllowSwitchOff"); m_DefaultToggle = serializedObject.FindProperty("m_DefaultToggle"); _reorderableList = new ReorderableList(serializedObject, m_Toggles, true, true, true, true) { drawHeaderCallback = DrawHeader, drawElementCallback = DrawElement, onRemoveCallback = OnRemoveList, onChangedCallback = OnChanged, displayAdd = false, }; } public override void OnInspectorGUI() { serializedObject.Update(); EditorGUILayout.PropertyField(m_AllowSwitchOff); // Default selector: only show toggles that are currently in the group's m_Toggles list DrawDefaultToggleSelector(); bool isPlaying = Application.isPlaying || EditorApplication.isPlaying; _reorderableList.draggable = !isPlaying; _reorderableList.displayAdd = !isPlaying; _reorderableList.displayRemove = !isPlaying; bool prevEnabled = GUI.enabled; if (isPlaying) GUI.enabled = false; _reorderableList.DoLayoutList(); GUI.enabled = prevEnabled; serializedObject.ApplyModifiedProperties(); // 在编辑器下尽量实时同步状态 if (!Application.isPlaying) { // 保证序列化数据写入后同步 group 状态 _target.EnsureValidState(); } } private void DrawDefaultToggleSelector() { // Build a list of current toggles (non-null) List toggles = new List(); for (int i = 0; i < m_Toggles.arraySize; i++) { var elem = m_Toggles.GetArrayElementAtIndex(i); var t = elem.objectReferenceValue as UXToggle; if (t != null) toggles.Add(t); } // Prepare options array with a "None" entry string[] options = new string[toggles.Count + 1]; options[0] = ""; for (int i = 0; i < toggles.Count; i++) { options[i + 1] = string.Format("[{0}] {1}", i, toggles[i] != null ? toggles[i].name : "Null"); } // Determine current index UXToggle currentDefault = m_DefaultToggle.objectReferenceValue as UXToggle; int currentIndex = 0; if (currentDefault != null) { int found = toggles.IndexOf(currentDefault); if (found >= 0) currentIndex = found + 1; // +1 because 0 is else { // Current default is not in the list -> clear it m_DefaultToggle.objectReferenceValue = null; serializedObject.ApplyModifiedProperties(); currentIndex = 0; } } EditorGUI.BeginChangeCheck(); int newIndex = EditorGUILayout.Popup("Default Toggle", currentIndex, options); if (EditorGUI.EndChangeCheck()) { UXToggle newDefault = null; if (newIndex > 0) newDefault = toggles[newIndex - 1]; m_DefaultToggle.objectReferenceValue = newDefault; serializedObject.ApplyModifiedProperties(); // 如果选择了一个非空默认项,则在 allowSwitchOff == false 时将其设为选中状态(并通知组) if (newDefault != null) { // 确保该 toggle 在 group 中(理论上应该如此) if (!_target.ContainsToggle(newDefault)) { _target.RegisterToggle(newDefault); } if (!_target.allowSwitchOff) { // 通过 SerializedObject 修改 UXToggle 的 m_IsOn,避免触发不必要的回调 SerializedObject so = new SerializedObject(newDefault); var isOnProp = so.FindProperty("m_IsOn"); isOnProp.boolValue = true; so.ApplyModifiedProperties(); // 通知组同步其余项 _target.NotifyToggleOn(newDefault); } } else { // 选择 None:不自动切换状态。但如果组不允许 all-off,则会在 EnsureValidState 中被处理 } } } private void DrawHeader(Rect rect) { EditorGUI.LabelField(rect, "Toggles", EditorStyles.boldLabel); } // 记录旧的引用用于侦测变化 private UXToggle previousRef; private void DrawElement(Rect rect, int index, bool isActive, bool isFocused) { SerializedProperty element = m_Toggles.GetArrayElementAtIndex(index); rect.y += 2; Rect fieldRect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight); UXToggle oldButton = element.objectReferenceValue as UXToggle; string label = $"[{index}] {(oldButton != null ? oldButton.name : "Null")}"; EditorGUI.BeginChangeCheck(); var newRef = EditorGUI.ObjectField(fieldRect, label, oldButton, typeof(UXToggle), true) as UXToggle; if (EditorGUI.EndChangeCheck()) { // 先处理 Remove(旧值存在且不同) if (oldButton != null && oldButton != newRef) { OnRemove(oldButton); } // 再处理 Add(新值非空) if (newRef != null && oldButton != newRef) { OnAdd(newRef); } // 最后把引用写回去 element.objectReferenceValue = newRef; serializedObject.ApplyModifiedProperties(); } } private void OnAddList(ReorderableList list) { int newIndex = m_Toggles.arraySize; m_Toggles.arraySize++; serializedObject.ApplyModifiedProperties(); var newElem = m_Toggles.GetArrayElementAtIndex(newIndex); newElem.objectReferenceValue = null; serializedObject.ApplyModifiedProperties(); } private void OnRemoveList(ReorderableList list) { if (list.index < 0 || list.index >= m_Toggles.arraySize) return; var oldButton = m_Toggles.GetArrayElementAtIndex(list.index).objectReferenceValue as UXToggle; if (oldButton) { OnRemove(oldButton); } m_Toggles.DeleteArrayElementAtIndex(list.index); serializedObject.ApplyModifiedProperties(); } private void OnChanged(ReorderableList list) { serializedObject.ApplyModifiedProperties(); // 编辑器变动后同步 group 状态和默认项 if (!Application.isPlaying) _target.EnsureValidState(); } // ======================== // 自动调用的新增方法 // ======================== private void OnAdd(UXToggle toggle) { if (toggle == null) return; SerializedObject so = new SerializedObject(toggle); var groupProp = so.FindProperty("m_Group"); groupProp.objectReferenceValue = target; so.ApplyModifiedProperties(); UXGroup group = (UXGroup)target; group.RegisterToggle(toggle); // 添加后尽量同步组状态(处理默认项或冲突) if (!Application.isPlaying) group.EnsureValidState(); } private void OnRemove(UXToggle toggle) { if (toggle == null) return; SerializedObject so = new SerializedObject(toggle); var groupProp = so.FindProperty("m_Group"); UXGroup group = groupProp.objectReferenceValue as UXGroup; if (group != null) group.UnregisterToggle(toggle); groupProp.objectReferenceValue = null; so.ApplyModifiedProperties(); // 如果移除的正好是默认项,则清空默认选项 if (m_DefaultToggle != null && m_DefaultToggle.objectReferenceValue == toggle) { m_DefaultToggle.objectReferenceValue = null; serializedObject.ApplyModifiedProperties(); } if (!Application.isPlaying && _target != null) _target.EnsureValidState(); } } }