403 lines
15 KiB
C#
403 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--;
|
|||
|
|
}
|
|||
|
|
}
|