347 lines
14 KiB
C#
347 lines
14 KiB
C#
using System;
|
|
using System.Linq;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
|
|
namespace Dott.Editor
|
|
{
|
|
[CustomEditor(typeof(DOTweenFrame))]
|
|
public class DOTweenFrameEditor : UnityEditor.Editor
|
|
{
|
|
public override void OnInspectorGUI()
|
|
{
|
|
serializedObject.Update();
|
|
|
|
var idProperty = serializedObject.FindProperty("id");
|
|
var delayProperty = serializedObject.FindProperty("delay");
|
|
var properties = serializedObject.FindProperty("properties");
|
|
|
|
EditorGUILayout.Space();
|
|
EditorGUILayout.PropertyField(idProperty, new GUIContent("ID"));
|
|
EditorGUILayout.PropertyField(delayProperty);
|
|
EditorGUILayout.Space();
|
|
|
|
for (var i = 0; i < properties.arraySize; i++)
|
|
{
|
|
OnPropertyGUI(properties, i);
|
|
}
|
|
|
|
if (GUILayout.Button("Add"))
|
|
{
|
|
properties.arraySize++;
|
|
|
|
// Unity duplicates the last element when increasing arraySize, so reset the new one manually
|
|
serializedObject.ApplyModifiedProperties();
|
|
((DOTweenFrame)target).Properties[properties.arraySize - 1] = new DOTweenFrame.FrameProperty();
|
|
}
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
}
|
|
|
|
private static void OnPropertyGUI(SerializedProperty properties, int index)
|
|
{
|
|
var boxStyle = new GUIStyle(GUI.skin.box)
|
|
{
|
|
padding = new RectOffset(4, 0, 6, 6),
|
|
margin = new RectOffset(0, 0, 0, 8)
|
|
};
|
|
|
|
var current = new CurrentProperty(properties, index);
|
|
|
|
Rect targetRect;
|
|
using (new GUILayout.VerticalScope(boxStyle))
|
|
using (new LabelWidthScope(NARROW_LABEL_WIDTH))
|
|
{
|
|
Component targetComponent;
|
|
using (new EditorGUILayout.HorizontalScope())
|
|
{
|
|
EditorGUILayout.PropertyField(current.TargetGameObjectProp, new GUIContent("Target"));
|
|
targetRect = GUILayoutUtility.GetLastRect();
|
|
targetComponent = TargetComponent(current);
|
|
}
|
|
|
|
if (current.TargetGameObject != null)
|
|
{
|
|
EditorGUILayout.PropertyField(current.PropertyTypeProp);
|
|
}
|
|
|
|
EndValue(current, targetComponent);
|
|
}
|
|
|
|
MenuButton(current, targetRect, boxStyle.padding);
|
|
}
|
|
|
|
private static void MenuButton(CurrentProperty current, Rect targetRect, RectOffset boxPadding)
|
|
{
|
|
var icon = EditorGUIUtility.IconContent("_Menu");
|
|
|
|
var removeButtonRect = new Rect(targetRect.xMin - boxPadding.left - MENU_BUTTON_SIZE, targetRect.y, MENU_BUTTON_SIZE, MENU_BUTTON_SIZE);
|
|
if (GUI.Button(removeButtonRect, icon, EditorStyles.iconButton))
|
|
{
|
|
var menu = new GenericMenu();
|
|
menu.AddItem(new GUIContent("Remove"), false, () =>
|
|
{
|
|
current.Properties.DeleteArrayElementAtIndex(current.Index);
|
|
current.Properties.serializedObject.ApplyModifiedProperties();
|
|
});
|
|
menu.ShowAsContext();
|
|
}
|
|
}
|
|
|
|
private static Component TargetComponent(CurrentProperty current)
|
|
{
|
|
var targetProperty = current.TargetProp;
|
|
var targetGameObject = current.TargetGameObject;
|
|
var propertyType = current.PropertyType;
|
|
|
|
if (targetGameObject == null || propertyType == DOTweenFrame.FrameProperty.PropertyType.None)
|
|
{
|
|
targetProperty.objectReferenceValue = null;
|
|
return null;
|
|
}
|
|
|
|
var components = FindTargetComponents(targetGameObject, propertyType);
|
|
if (components.Length == 0)
|
|
{
|
|
EditorGUILayout.HelpBox("No suitable target component found", MessageType.Error);
|
|
targetProperty.objectReferenceValue = null;
|
|
return null;
|
|
}
|
|
|
|
Component component;
|
|
var showPopupForSingleComponent = propertyType == DOTweenFrame.FrameProperty.PropertyType.Enabled;
|
|
if (components.Length == 1 && !showPopupForSingleComponent)
|
|
{
|
|
targetProperty.objectReferenceValue = component = components[0];
|
|
return component;
|
|
}
|
|
|
|
var componentNames = components.Select(c => c.GetType().Name).ToArray();
|
|
var selectedIndex = Array.IndexOf(components, (Component)targetProperty.objectReferenceValue);
|
|
if (selectedIndex == -1)
|
|
{
|
|
selectedIndex = 0;
|
|
}
|
|
|
|
selectedIndex = EditorGUILayout.Popup(selectedIndex, componentNames, GUILayout.MaxWidth(104f));
|
|
targetProperty.objectReferenceValue = component = components[selectedIndex];
|
|
return component;
|
|
}
|
|
|
|
private static void EndValue(CurrentProperty current, Component targetComponent)
|
|
{
|
|
if (targetComponent == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var vector3Property = current.EndValueVector3Prop;
|
|
var floatProperty = current.EndValueFloatProp;
|
|
var colorProperty = current.EndValueColorProp;
|
|
|
|
switch (current.PropertyType)
|
|
{
|
|
case DOTweenFrame.FrameProperty.PropertyType.Position:
|
|
case DOTweenFrame.FrameProperty.PropertyType.LocalPosition:
|
|
{
|
|
EditorGUILayout.PropertyField(vector3Property, new GUIContent("Position"));
|
|
break;
|
|
}
|
|
|
|
case DOTweenFrame.FrameProperty.PropertyType.Scale:
|
|
{
|
|
ScalePropertyField(current);
|
|
break;
|
|
}
|
|
|
|
case DOTweenFrame.FrameProperty.PropertyType.Fade:
|
|
{
|
|
EditorGUILayout.PropertyField(floatProperty, new GUIContent("Alpha"));
|
|
floatProperty.floatValue = Mathf.Clamp(floatProperty.floatValue, 0f, 1f);
|
|
break;
|
|
}
|
|
|
|
case DOTweenFrame.FrameProperty.PropertyType.Color:
|
|
EditorGUILayout.PropertyField(colorProperty, new GUIContent("Color"));
|
|
break;
|
|
|
|
case DOTweenFrame.FrameProperty.PropertyType.Active:
|
|
case DOTweenFrame.FrameProperty.PropertyType.Enabled:
|
|
EditorGUILayout.PropertyField(current.OptionalBoolProp, new GUIContent("Active"));
|
|
break;
|
|
|
|
case DOTweenFrame.FrameProperty.PropertyType.None:
|
|
break;
|
|
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(current.PropertyType), current.PropertyType, null);
|
|
}
|
|
|
|
IsRelativeField(current);
|
|
}
|
|
|
|
private static void IsRelativeField(CurrentProperty current)
|
|
{
|
|
switch (current.PropertyType)
|
|
{
|
|
case DOTweenFrame.FrameProperty.PropertyType.Position:
|
|
case DOTweenFrame.FrameProperty.PropertyType.LocalPosition:
|
|
case DOTweenFrame.FrameProperty.PropertyType.Scale:
|
|
EditorGUI.indentLevel++;
|
|
EditorGUILayout.PropertyField(current.IsRelativeProp);
|
|
EditorGUI.indentLevel--;
|
|
break;
|
|
|
|
case DOTweenFrame.FrameProperty.PropertyType.None:
|
|
case DOTweenFrame.FrameProperty.PropertyType.Fade:
|
|
case DOTweenFrame.FrameProperty.PropertyType.Color:
|
|
case DOTweenFrame.FrameProperty.PropertyType.Active:
|
|
case DOTweenFrame.FrameProperty.PropertyType.Enabled:
|
|
break;
|
|
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(current.PropertyType), current.PropertyType, null);
|
|
}
|
|
}
|
|
|
|
private static void ScalePropertyField(CurrentProperty current)
|
|
{
|
|
var uniformScaleButtonStyle = new GUIStyle("FloatFieldLinkButton");
|
|
var flexibleWidthOption = GUILayout.MinWidth(0f);
|
|
|
|
var uniformProperty = current.OptionalBoolProp;
|
|
var vector3Property = current.EndValueVector3Prop;
|
|
|
|
var uniformScale = uniformProperty.boolValue;
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
|
|
GUILayout.Label("Scale", GUILayout.MaxWidth(NARROW_LABEL_WIDTH));
|
|
var lastRect = GUILayoutUtility.GetLastRect();
|
|
var toggleRect = new Rect(lastRect.xMax - UNIFORM_SCALE_BUTTON_WIDTH, lastRect.y, UNIFORM_SCALE_BUTTON_WIDTH, lastRect.height);
|
|
uniformScale = EditorGUI.Toggle(toggleRect, uniformScale, uniformScaleButtonStyle);
|
|
|
|
uniformProperty.boolValue = uniformScale;
|
|
if (!uniformScale)
|
|
{
|
|
EditorGUILayout.PropertyField(vector3Property, GUIContent.none);
|
|
EditorGUILayout.EndHorizontal();
|
|
return;
|
|
}
|
|
|
|
using (new LabelWidthScope(AXIS_LABEL_WIDTH))
|
|
{
|
|
var scale = vector3Property.vector3Value.x;
|
|
scale = EditorGUILayout.FloatField("X", scale, flexibleWidthOption);
|
|
vector3Property.vector3Value = Vector3.one * scale;
|
|
|
|
using (new EditorGUI.DisabledScope(true))
|
|
{
|
|
EditorGUILayout.FloatField("Y", scale, flexibleWidthOption);
|
|
EditorGUILayout.FloatField("Z", scale, flexibleWidthOption);
|
|
}
|
|
}
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
}
|
|
|
|
private static Component[] FindTargetComponents(GameObject targetGameObject, DOTweenFrame.FrameProperty.PropertyType propertyType)
|
|
{
|
|
switch (propertyType)
|
|
{
|
|
case DOTweenFrame.FrameProperty.PropertyType.Position:
|
|
case DOTweenFrame.FrameProperty.PropertyType.LocalPosition:
|
|
case DOTweenFrame.FrameProperty.PropertyType.Scale:
|
|
case DOTweenFrame.FrameProperty.PropertyType.Active:
|
|
return new Component[] { targetGameObject.GetComponent<Transform>() };
|
|
|
|
case DOTweenFrame.FrameProperty.PropertyType.Fade:
|
|
{
|
|
var graphic = targetGameObject.GetComponent<Graphic>();
|
|
var canvasGroup = targetGameObject.GetComponent<CanvasGroup>();
|
|
return new Component[] { graphic, canvasGroup }.Where(c => c != null).ToArray();
|
|
}
|
|
|
|
case DOTweenFrame.FrameProperty.PropertyType.Color:
|
|
{
|
|
var graphic = targetGameObject.GetComponent<Graphic>();
|
|
var camera = targetGameObject.GetComponent<Camera>();
|
|
return new Component[] { graphic, camera }.Where(c => c != null).ToArray();
|
|
}
|
|
|
|
case DOTweenFrame.FrameProperty.PropertyType.Enabled:
|
|
return targetGameObject.GetComponents<Behaviour>().Cast<Component>().ToArray();
|
|
|
|
case DOTweenFrame.FrameProperty.PropertyType.None:
|
|
default:
|
|
return Array.Empty<Component>();
|
|
}
|
|
}
|
|
|
|
private struct CurrentProperty
|
|
{
|
|
public readonly SerializedProperty Properties;
|
|
public readonly int Index;
|
|
|
|
public readonly SerializedProperty TargetGameObjectProp;
|
|
public readonly SerializedProperty PropertyTypeProp;
|
|
public readonly SerializedProperty TargetProp;
|
|
public readonly SerializedProperty IsRelativeProp;
|
|
|
|
public readonly SerializedProperty EndValueVector3Prop;
|
|
public readonly SerializedProperty EndValueFloatProp;
|
|
public readonly SerializedProperty EndValueColorProp;
|
|
public readonly SerializedProperty OptionalBoolProp;
|
|
|
|
public readonly DOTweenFrame.FrameProperty.PropertyType PropertyType => (DOTweenFrame.FrameProperty.PropertyType)PropertyTypeProp.intValue;
|
|
public readonly GameObject TargetGameObject => TargetGameObjectProp.objectReferenceValue as GameObject;
|
|
|
|
public CurrentProperty(SerializedProperty properties, int index)
|
|
{
|
|
Properties = properties;
|
|
Index = index;
|
|
var property = properties.GetArrayElementAtIndex(index);
|
|
|
|
TargetGameObjectProp = property.FindPropertyRelative("TargetGameObject");
|
|
PropertyTypeProp = property.FindPropertyRelative("Property");
|
|
TargetProp = property.FindPropertyRelative("Target");
|
|
IsRelativeProp = property.FindPropertyRelative("IsRelative");
|
|
|
|
EndValueVector3Prop = property.FindPropertyRelative("EndValueVector3");
|
|
EndValueFloatProp = property.FindPropertyRelative("EndValueFloat");
|
|
EndValueColorProp = property.FindPropertyRelative("EndValueColor");
|
|
OptionalBoolProp = property.FindPropertyRelative("OptionalBool");
|
|
}
|
|
}
|
|
|
|
#region Styles
|
|
|
|
private const float NARROW_LABEL_WIDTH = 90f;
|
|
private const int AXIS_LABEL_WIDTH = 12;
|
|
private const float UNIFORM_SCALE_BUTTON_WIDTH = 18f;
|
|
private const float MENU_BUTTON_SIZE = 16f;
|
|
|
|
#endregion
|
|
|
|
#region GUI
|
|
|
|
private class LabelWidthScope : GUI.Scope
|
|
{
|
|
private readonly float prevLabelWidth;
|
|
|
|
public LabelWidthScope(float labelWidth)
|
|
{
|
|
prevLabelWidth = EditorGUIUtility.labelWidth;
|
|
EditorGUIUtility.labelWidth = labelWidth;
|
|
}
|
|
|
|
protected override void CloseScope()
|
|
{
|
|
EditorGUIUtility.labelWidth = prevLabelWidth;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |