com.alicizax.unity.ui.exten.../Editor/UX/Button/UXButtonEditor.cs

402 lines
15 KiB
C#
Raw Normal View History

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