com.alicizax.unity.ui.exten.../Editor/UX/Button/UXButtonEditor.cs
2025-12-15 13:42:08 +08:00

402 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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--;
}
}