402 lines
15 KiB
C#
402 lines
15 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using AlicizaX.UI.Extension;
|
||
using UnityEditor;
|
||
using UnityEditor.SceneManagement;
|
||
using UnityEditorInternal;
|
||
using UnityEditor.UI;
|
||
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
using AnimatorControllerParameterType = UnityEngine.AnimatorControllerParameterType;
|
||
|
||
[CanEditMultipleObjects]
|
||
[CustomEditor(typeof(UXButton), true)]
|
||
internal class UXButtonEditor : UXSelectableEditor
|
||
{
|
||
private enum TabType
|
||
{
|
||
Image,
|
||
Sound,
|
||
Event
|
||
}
|
||
|
||
private SerializedProperty m_Mode;
|
||
private SerializedProperty m_OnValueChanged;
|
||
private SerializedProperty m_OnClick;
|
||
private SerializedProperty m_UXGroup;
|
||
private SerializedProperty m_ChildTransitions;
|
||
|
||
private ReorderableList m_ChildTransitionList;
|
||
|
||
private static Color darkZebraEven = new Color(0.22f, 0.22f, 0.22f);
|
||
private static Color darkZebraOdd = new Color(0.27f, 0.27f, 0.27f);
|
||
|
||
private SerializedProperty hoverAudioClip;
|
||
private SerializedProperty clickAudioClip;
|
||
|
||
private UXButton mTarget;
|
||
|
||
protected override void OnEnable()
|
||
{
|
||
base.OnEnable();
|
||
mTarget = (UXButton)target;
|
||
|
||
m_UXGroup = serializedObject.FindProperty("m_UXGroup");
|
||
m_Mode = serializedObject.FindProperty("m_Mode");
|
||
m_OnValueChanged = serializedObject.FindProperty("m_OnValueChanged");
|
||
m_OnClick = serializedObject.FindProperty("m_OnClick");
|
||
m_ChildTransitions = serializedObject.FindProperty("m_ChildTransitions");
|
||
hoverAudioClip = serializedObject.FindProperty("hoverAudioClip");
|
||
clickAudioClip = serializedObject.FindProperty("clickAudioClip");
|
||
|
||
CreateChildTransitionList();
|
||
|
||
// 不再 Register Image(基类已经创建),改为 Append 到 base 的 Image 页签
|
||
AppendToTab("Image", DrawImageHead, false);
|
||
AppendToTab("Image", DrawImageBottom);
|
||
|
||
// 另外注册独立的 Sound / Event 页签
|
||
RegisterTab("Sound", "d_AudioSource Icon", DrawSoundTab);
|
||
RegisterTab("Event", "EventTrigger Icon", DrawEventTab);
|
||
}
|
||
|
||
protected override void OnDisable()
|
||
{
|
||
// 移除之前追加到 Image 页签的回调,避免残留
|
||
RemoveCallbackFromTab("Image", DrawImageHead);
|
||
UnregisterTab("Sound");
|
||
UnregisterTab("Event");
|
||
|
||
base.OnDisable();
|
||
}
|
||
|
||
private void CreateChildTransitionList()
|
||
{
|
||
m_ChildTransitionList = new ReorderableList(serializedObject, m_ChildTransitions, true, false, true, true);
|
||
|
||
m_ChildTransitionList.drawElementBackgroundCallback = (rect, index, isActive, isFocused) =>
|
||
{
|
||
var background = index % 2 == 0 ? darkZebraEven : darkZebraOdd;
|
||
EditorGUI.DrawRect(rect, background);
|
||
};
|
||
|
||
m_ChildTransitionList.drawElementCallback = (rect, index, isActive, isFocused) =>
|
||
{
|
||
var element = m_ChildTransitionList.serializedProperty.GetArrayElementAtIndex(index);
|
||
rect.y += 2;
|
||
|
||
string elementTitle = $"Null Transition";
|
||
var targetProp = element.FindPropertyRelative("targetGraphic");
|
||
if (targetProp.objectReferenceValue != null)
|
||
elementTitle = targetProp.objectReferenceValue.name;
|
||
|
||
EditorGUI.LabelField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight),
|
||
elementTitle, EditorStyles.boldLabel);
|
||
|
||
rect.y += EditorGUIUtility.singleLineHeight + 2;
|
||
DrawTransitionData(new Rect(rect.x, rect.y, rect.width, 0), element, true);
|
||
};
|
||
|
||
m_ChildTransitionList.elementHeightCallback = (index) =>
|
||
{
|
||
return EditorGUIUtility.singleLineHeight +
|
||
CalculateTransitionDataHeight(m_ChildTransitionList.serializedProperty.GetArrayElementAtIndex(index)) +
|
||
10;
|
||
};
|
||
|
||
m_ChildTransitionList.onAddCallback = (list) =>
|
||
{
|
||
list.serializedProperty.arraySize++;
|
||
serializedObject.ApplyModifiedProperties();
|
||
};
|
||
|
||
m_ChildTransitionList.onRemoveCallback = (list) => { ReorderableList.defaultBehaviours.DoRemoveButton(list); };
|
||
}
|
||
|
||
|
||
// 追加到 base Image 页签的内容
|
||
private void DrawImageHead()
|
||
{
|
||
EditorGUI.BeginDisabledGroup(EditorApplication.isPlaying);
|
||
var modeType = (ButtonModeType)EditorGUILayout.EnumPopup("Mode", (ButtonModeType)m_Mode.enumValueIndex);
|
||
if (modeType != (ButtonModeType)m_Mode.enumValueIndex)
|
||
{
|
||
if (modeType == ButtonModeType.Normal)
|
||
{
|
||
ResetEventProperty(m_OnValueChanged);
|
||
m_UXGroup.objectReferenceValue = null;
|
||
}
|
||
else
|
||
{
|
||
ResetEventProperty(m_OnClick);
|
||
}
|
||
|
||
m_Mode.enumValueIndex = (int)modeType;
|
||
}
|
||
|
||
EditorGUI.EndDisabledGroup();
|
||
|
||
|
||
|
||
}
|
||
|
||
private void DrawImageBottom()
|
||
{
|
||
GUILayout.Space(5);
|
||
|
||
DrawChildTransitions();
|
||
|
||
GUILayout.Space(1);
|
||
DrawBasicSettings();
|
||
}
|
||
|
||
private void DrawSoundTab()
|
||
{
|
||
GUILayoutHelper.DrawProperty(hoverAudioClip, customSkin, "Hover Sound", "Play", () =>
|
||
{
|
||
if (hoverAudioClip.objectReferenceValue != null)
|
||
{
|
||
PlayAudio((AudioClip)hoverAudioClip.objectReferenceValue);
|
||
}
|
||
});
|
||
GUILayoutHelper.DrawProperty(clickAudioClip, customSkin, "Click Sound", "Play", () =>
|
||
{
|
||
if (clickAudioClip.objectReferenceValue != null)
|
||
{
|
||
PlayAudio((AudioClip)clickAudioClip.objectReferenceValue);
|
||
}
|
||
});
|
||
}
|
||
|
||
protected virtual void DrawEventTab()
|
||
{
|
||
if (m_Mode.enumValueIndex == (int)ButtonModeType.Toggle)
|
||
{
|
||
EditorGUILayout.Space();
|
||
EditorGUILayout.PropertyField(m_OnValueChanged);
|
||
}
|
||
else
|
||
{
|
||
EditorGUILayout.Space();
|
||
EditorGUILayout.PropertyField(m_OnClick);
|
||
}
|
||
|
||
EditorGUILayout.Separator();
|
||
}
|
||
|
||
// helpers kept from original file
|
||
private void ResetEventProperty(SerializedProperty property)
|
||
{
|
||
SerializedProperty persistentCalls = property.FindPropertyRelative("m_PersistentCalls");
|
||
SerializedProperty calls = persistentCalls.FindPropertyRelative("m_Calls");
|
||
calls.arraySize = 0;
|
||
property.serializedObject.ApplyModifiedProperties();
|
||
}
|
||
|
||
private void PlayAudio(AudioClip clip)
|
||
{
|
||
if (clip != null)
|
||
{
|
||
ExtensionHelper.PreviewAudioClip(clip);
|
||
}
|
||
}
|
||
|
||
private void DrawBasicSettings()
|
||
{
|
||
if (m_Mode.enumValueIndex == (int)ButtonModeType.Toggle)
|
||
{
|
||
GUILayoutHelper.DrawProperty<UXGroup>(m_UXGroup, customSkin, "UXGroup", (oldValue, newValue) =>
|
||
{
|
||
UXButton self = target as UXButton;
|
||
if (oldValue != null)
|
||
{
|
||
oldValue.UnregisterButton(self);
|
||
}
|
||
|
||
if (newValue != null)
|
||
{
|
||
newValue.RegisterButton(self);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
private void DrawChildTransitions()
|
||
{
|
||
m_ChildTransitionList.DoLayoutList();
|
||
}
|
||
|
||
// CalculateTransitionDataHeight & DrawTransitionData remain the same as earlier to preserve child-only Animation restriction and warnings.
|
||
private float CalculateTransitionDataHeight(SerializedProperty transitionData)
|
||
{
|
||
float height = 0;
|
||
SerializedProperty transition = transitionData.FindPropertyRelative("transition");
|
||
var currentTransition = GetTransition(transition);
|
||
|
||
bool isChild = m_ChildTransitions != null &&
|
||
!string.IsNullOrEmpty(m_ChildTransitions.propertyPath) &&
|
||
transitionData.propertyPath.StartsWith(m_ChildTransitions.propertyPath);
|
||
|
||
if (isChild && currentTransition == Selectable.Transition.Animation)
|
||
{
|
||
currentTransition = Selectable.Transition.ColorTint;
|
||
}
|
||
|
||
height += EditorGUIUtility.singleLineHeight * 1.5f;
|
||
height += EditorGUIUtility.singleLineHeight;
|
||
|
||
SerializedProperty targetGraphic = transitionData.FindPropertyRelative("targetGraphic");
|
||
var graphic = targetGraphic.objectReferenceValue as Graphic;
|
||
var animator = graphic != null ? graphic.GetComponent<Animator>() : null;
|
||
|
||
switch (currentTransition)
|
||
{
|
||
case Selectable.Transition.ColorTint:
|
||
if (graphic == null) height += EditorGUIUtility.singleLineHeight;
|
||
break;
|
||
case Selectable.Transition.SpriteSwap:
|
||
if (!(graphic is Image)) height += EditorGUIUtility.singleLineHeight;
|
||
break;
|
||
case Selectable.Transition.Animation:
|
||
if (animator == null) height += EditorGUIUtility.singleLineHeight;
|
||
break;
|
||
}
|
||
|
||
switch (currentTransition)
|
||
{
|
||
case Selectable.Transition.ColorTint:
|
||
height += EditorGUI.GetPropertyHeight(transitionData.FindPropertyRelative("colors"));
|
||
break;
|
||
case Selectable.Transition.SpriteSwap:
|
||
height += EditorGUI.GetPropertyHeight(transitionData.FindPropertyRelative("spriteState"));
|
||
break;
|
||
case Selectable.Transition.Animation:
|
||
height += EditorGUI.GetPropertyHeight(transitionData.FindPropertyRelative("animationTriggers"));
|
||
break;
|
||
}
|
||
|
||
return height;
|
||
}
|
||
|
||
private void DrawTransitionData(Rect position, SerializedProperty transitionData, bool isChild = false)
|
||
{
|
||
SerializedProperty targetGraphic = transitionData.FindPropertyRelative("targetGraphic");
|
||
SerializedProperty transition = transitionData.FindPropertyRelative("transition");
|
||
SerializedProperty colorBlock = transitionData.FindPropertyRelative("colors");
|
||
SerializedProperty spriteState = transitionData.FindPropertyRelative("spriteState");
|
||
SerializedProperty animationTriggers = transitionData.FindPropertyRelative("animationTriggers");
|
||
|
||
EditorGUI.indentLevel++;
|
||
float lineHeight = EditorGUIUtility.singleLineHeight;
|
||
float spacing = 2f;
|
||
float y = position.y;
|
||
|
||
var currentTransition = GetTransition(transition);
|
||
|
||
if (isChild && currentTransition == Selectable.Transition.Animation)
|
||
{
|
||
Rect warningRect = new Rect(position.x, y, position.width, lineHeight);
|
||
EditorGUI.HelpBox(warningRect, "Animation 过渡仅允许用于 Main Transition,子 Transition 不支持 Animation(已显示为 ColorTint)", MessageType.Warning);
|
||
y += lineHeight + spacing;
|
||
currentTransition = Selectable.Transition.ColorTint;
|
||
}
|
||
|
||
switch (currentTransition)
|
||
{
|
||
case Selectable.Transition.ColorTint:
|
||
case Selectable.Transition.SpriteSwap:
|
||
Rect targetRect = new Rect(position.x, y, position.width, lineHeight);
|
||
EditorGUI.PropertyField(targetRect, targetGraphic);
|
||
y += lineHeight + spacing;
|
||
break;
|
||
}
|
||
|
||
Rect transitionRect = new Rect(position.x, y, position.width, lineHeight);
|
||
|
||
if (isChild)
|
||
{
|
||
string[] options = new[] { "None", "Color Tint", "Sprite Swap" };
|
||
int[] values = new[] { (int)Selectable.Transition.None, (int)Selectable.Transition.ColorTint, (int)Selectable.Transition.SpriteSwap };
|
||
|
||
int curVal = transition.enumValueIndex;
|
||
int selIdx = Array.IndexOf(values, curVal);
|
||
if (selIdx < 0) selIdx = 0;
|
||
|
||
selIdx = EditorGUI.Popup(transitionRect, "Transition", selIdx, options);
|
||
transition.enumValueIndex = values[selIdx];
|
||
|
||
currentTransition = GetTransition(transition);
|
||
y += lineHeight + spacing;
|
||
}
|
||
else
|
||
{
|
||
EditorGUI.PropertyField(transitionRect, transition);
|
||
y += lineHeight + spacing;
|
||
}
|
||
|
||
var graphic = targetGraphic.objectReferenceValue as Graphic;
|
||
var animator = graphic != null ? graphic.GetComponent<Animator>() : null;
|
||
|
||
switch (currentTransition)
|
||
{
|
||
case Selectable.Transition.ColorTint:
|
||
if (graphic == null)
|
||
{
|
||
Rect warningRect = new Rect(position.x, y, position.width, lineHeight);
|
||
EditorGUI.HelpBox(warningRect, "需要Graphic组件来使用颜色过渡", MessageType.Warning);
|
||
y += lineHeight + spacing;
|
||
}
|
||
|
||
break;
|
||
|
||
case Selectable.Transition.SpriteSwap:
|
||
if (!(graphic is Image))
|
||
{
|
||
Rect warningRect = new Rect(position.x, y, position.width, lineHeight);
|
||
EditorGUI.HelpBox(warningRect, "需要Image组件来使用精灵切换", MessageType.Warning);
|
||
y += lineHeight + spacing;
|
||
}
|
||
|
||
break;
|
||
case Selectable.Transition.Animation:
|
||
if (animator == null)
|
||
{
|
||
Rect warningRect = new Rect(position.x, y, position.width, lineHeight);
|
||
EditorGUI.HelpBox(warningRect, "需要Animator组件来使用动画切换", MessageType.Warning);
|
||
y += lineHeight + spacing;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
switch (currentTransition)
|
||
{
|
||
case Selectable.Transition.ColorTint:
|
||
CheckAndSetColorDefaults(colorBlock, targetGraphic);
|
||
Rect colorRect = new Rect(position.x, y, position.width, EditorGUI.GetPropertyHeight(colorBlock));
|
||
EditorGUI.PropertyField(colorRect, colorBlock);
|
||
break;
|
||
|
||
case Selectable.Transition.SpriteSwap:
|
||
CheckAndSetColorDefaults(colorBlock, targetGraphic);
|
||
Rect spriteRect = new Rect(position.x, y, position.width, EditorGUI.GetPropertyHeight(spriteState));
|
||
EditorGUI.PropertyField(spriteRect, spriteState);
|
||
break;
|
||
case Selectable.Transition.Animation:
|
||
Rect animRect = new Rect(position.x, y, position.width, EditorGUI.GetPropertyHeight(animationTriggers));
|
||
EditorGUI.PropertyField(animRect, animationTriggers);
|
||
break;
|
||
}
|
||
|
||
if (graphic != null && currentTransition != (Selectable.Transition)transition.enumValueIndex &&
|
||
((Selectable.Transition)transition.enumValueIndex == Selectable.Transition.Animation ||
|
||
(Selectable.Transition)transition.enumValueIndex == Selectable.Transition.None))
|
||
{
|
||
graphic.canvasRenderer.SetColor(Color.white);
|
||
}
|
||
|
||
EditorGUI.indentLevel--;
|
||
}
|
||
}
|