summaryrefslogtreecommitdiff
path: root/Assets/uGUI-2017.1/Editor/UI/SelectableEditor.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Assets/uGUI-2017.1/Editor/UI/SelectableEditor.cs')
-rw-r--r--Assets/uGUI-2017.1/Editor/UI/SelectableEditor.cs370
1 files changed, 370 insertions, 0 deletions
diff --git a/Assets/uGUI-2017.1/Editor/UI/SelectableEditor.cs b/Assets/uGUI-2017.1/Editor/UI/SelectableEditor.cs
new file mode 100644
index 0000000..d3329cc
--- /dev/null
+++ b/Assets/uGUI-2017.1/Editor/UI/SelectableEditor.cs
@@ -0,0 +1,370 @@
+using System.Collections.Generic;
+using System.Text;
+using System.Linq;
+using UnityEngine;
+using UnityEngine.UI;
+using UnityEditor.AnimatedValues;
+
+namespace UnityEditor.UI
+{
+ [CustomEditor(typeof(Selectable), true)]
+ public class SelectableEditor : Editor
+ {
+ SerializedProperty m_Script;
+ SerializedProperty m_InteractableProperty;
+ SerializedProperty m_TargetGraphicProperty;
+ SerializedProperty m_TransitionProperty;
+ SerializedProperty m_ColorBlockProperty;
+ SerializedProperty m_SpriteStateProperty;
+ SerializedProperty m_AnimTriggerProperty;
+ SerializedProperty m_NavigationProperty;
+
+ GUIContent m_VisualizeNavigation = new GUIContent("Visualize", "Show navigation flows between selectable UI elements.");
+
+ AnimBool m_ShowColorTint = new AnimBool();
+ AnimBool m_ShowSpriteTrasition = new AnimBool();
+ AnimBool m_ShowAnimTransition = new AnimBool();
+
+ private static List<SelectableEditor> s_Editors = new List<SelectableEditor>();
+ private static bool s_ShowNavigation = false;
+ private static string s_ShowNavigationKey = "SelectableEditor.ShowNavigation";
+
+ // Whenever adding new SerializedProperties to the Selectable and SelectableEditor
+ // Also update this guy in OnEnable. This makes the inherited classes from Selectable not require a CustomEditor.
+ private string[] m_PropertyPathToExcludeForChildClasses;
+
+ protected virtual void OnEnable()
+ {
+ m_Script = serializedObject.FindProperty("m_Script");
+ m_InteractableProperty = serializedObject.FindProperty("m_Interactable");
+ m_TargetGraphicProperty = serializedObject.FindProperty("m_TargetGraphic");
+ m_TransitionProperty = serializedObject.FindProperty("m_Transition");
+ m_ColorBlockProperty = serializedObject.FindProperty("m_Colors");
+ m_SpriteStateProperty = serializedObject.FindProperty("m_SpriteState");
+ m_AnimTriggerProperty = serializedObject.FindProperty("m_AnimationTriggers");
+ m_NavigationProperty = serializedObject.FindProperty("m_Navigation");
+
+ m_PropertyPathToExcludeForChildClasses = new[]
+ {
+ m_Script.propertyPath,
+ m_NavigationProperty.propertyPath,
+ m_TransitionProperty.propertyPath,
+ m_ColorBlockProperty.propertyPath,
+ m_SpriteStateProperty.propertyPath,
+ m_AnimTriggerProperty.propertyPath,
+ m_InteractableProperty.propertyPath,
+ m_TargetGraphicProperty.propertyPath,
+ };
+
+ var trans = GetTransition(m_TransitionProperty);
+ m_ShowColorTint.value = (trans == Selectable.Transition.ColorTint);
+ m_ShowSpriteTrasition.value = (trans == Selectable.Transition.SpriteSwap);
+ m_ShowAnimTransition.value = (trans == Selectable.Transition.Animation);
+
+ m_ShowColorTint.valueChanged.AddListener(Repaint);
+ m_ShowSpriteTrasition.valueChanged.AddListener(Repaint);
+
+ s_Editors.Add(this);
+ RegisterStaticOnSceneGUI();
+
+ s_ShowNavigation = EditorPrefs.GetBool(s_ShowNavigationKey);
+ }
+
+ protected virtual void OnDisable()
+ {
+ m_ShowColorTint.valueChanged.RemoveListener(Repaint);
+ m_ShowSpriteTrasition.valueChanged.RemoveListener(Repaint);
+
+ s_Editors.Remove(this);
+ RegisterStaticOnSceneGUI();
+ }
+
+ private void RegisterStaticOnSceneGUI()
+ {
+ SceneView.onSceneGUIDelegate -= StaticOnSceneGUI;
+ if (s_Editors.Count > 0)
+ SceneView.onSceneGUIDelegate += StaticOnSceneGUI;
+ }
+
+ static Selectable.Transition GetTransition(SerializedProperty transition)
+ {
+ return (Selectable.Transition)transition.enumValueIndex;
+ }
+
+ public override void OnInspectorGUI()
+ {
+ serializedObject.Update();
+
+ EditorGUILayout.PropertyField(m_InteractableProperty);
+
+ var trans = GetTransition(m_TransitionProperty);
+
+ var graphic = m_TargetGraphicProperty.objectReferenceValue as Graphic;
+ if (graphic == null)
+ graphic = (target as Selectable).GetComponent<Graphic>();
+
+ var animator = (target as Selectable).GetComponent<Animator>();
+ m_ShowColorTint.target = (!m_TransitionProperty.hasMultipleDifferentValues && trans == Button.Transition.ColorTint);
+ m_ShowSpriteTrasition.target = (!m_TransitionProperty.hasMultipleDifferentValues && trans == Button.Transition.SpriteSwap);
+ m_ShowAnimTransition.target = (!m_TransitionProperty.hasMultipleDifferentValues && trans == Button.Transition.Animation);
+
+ EditorGUILayout.PropertyField(m_TransitionProperty);
+
+ ++EditorGUI.indentLevel;
+ {
+ if (trans == Selectable.Transition.ColorTint || trans == Selectable.Transition.SpriteSwap)
+ {
+ EditorGUILayout.PropertyField(m_TargetGraphicProperty);
+ }
+
+ switch (trans)
+ {
+ case Selectable.Transition.ColorTint:
+ if (graphic == null)
+ EditorGUILayout.HelpBox("You must have a Graphic target in order to use a color transition.", MessageType.Warning);
+ break;
+
+ case Selectable.Transition.SpriteSwap:
+ if (graphic as Image == null)
+ EditorGUILayout.HelpBox("You must have a Image target in order to use a sprite swap transition.", MessageType.Warning);
+ break;
+ }
+
+ if (EditorGUILayout.BeginFadeGroup(m_ShowColorTint.faded))
+ {
+ EditorGUILayout.PropertyField(m_ColorBlockProperty);
+ }
+ EditorGUILayout.EndFadeGroup();
+
+ if (EditorGUILayout.BeginFadeGroup(m_ShowSpriteTrasition.faded))
+ {
+ EditorGUILayout.PropertyField(m_SpriteStateProperty);
+ }
+ EditorGUILayout.EndFadeGroup();
+
+ if (EditorGUILayout.BeginFadeGroup(m_ShowAnimTransition.faded))
+ {
+ EditorGUILayout.PropertyField(m_AnimTriggerProperty);
+
+ if (animator == null || animator.runtimeAnimatorController == null)
+ {
+ Rect buttonRect = EditorGUILayout.GetControlRect();
+ buttonRect.xMin += EditorGUIUtility.labelWidth;
+ if (GUI.Button(buttonRect, "Auto Generate Animation", EditorStyles.miniButton))
+ {
+ var controller = GenerateSelectableAnimatorContoller((target as Selectable).animationTriggers, target as Selectable);
+ if (controller != null)
+ {
+ if (animator == null)
+ animator = (target as Selectable).gameObject.AddComponent<Animator>();
+
+ Animations.AnimatorController.SetAnimatorController(animator, controller);
+ }
+ }
+ }
+ }
+ EditorGUILayout.EndFadeGroup();
+ }
+ --EditorGUI.indentLevel;
+
+ EditorGUILayout.Space();
+
+ EditorGUILayout.PropertyField(m_NavigationProperty);
+
+ EditorGUI.BeginChangeCheck();
+ Rect toggleRect = EditorGUILayout.GetControlRect();
+ toggleRect.xMin += EditorGUIUtility.labelWidth;
+ s_ShowNavigation = GUI.Toggle(toggleRect, s_ShowNavigation, m_VisualizeNavigation, EditorStyles.miniButton);
+ if (EditorGUI.EndChangeCheck())
+ {
+ EditorPrefs.SetBool(s_ShowNavigationKey, s_ShowNavigation);
+ SceneView.RepaintAll();
+ }
+
+ // We do this here to avoid requiring the user to also write a Editor for their Selectable-derived classes.
+ // This way if we are on a derived class we dont draw anything else, otherwise draw the remaining properties.
+ ChildClassPropertiesGUI();
+
+ serializedObject.ApplyModifiedProperties();
+ }
+
+ // Draw the extra SerializedProperties of the child class.
+ // We need to make sure that m_PropertyPathToExcludeForChildClasses has all the Selectable properties and in the correct order.
+ // TODO: find a nicer way of doing this. (creating a InheritedEditor class that automagically does this)
+ private void ChildClassPropertiesGUI()
+ {
+ if (IsDerivedSelectableEditor())
+ return;
+
+ DrawPropertiesExcluding(serializedObject, m_PropertyPathToExcludeForChildClasses);
+ }
+
+ private bool IsDerivedSelectableEditor()
+ {
+ return GetType() != typeof(SelectableEditor);
+ }
+
+ private static Animations.AnimatorController GenerateSelectableAnimatorContoller(AnimationTriggers animationTriggers, Selectable target)
+ {
+ if (target == null)
+ return null;
+
+ // Where should we create the controller?
+ var path = GetSaveControllerPath(target);
+ if (string.IsNullOrEmpty(path))
+ return null;
+
+ // figure out clip names
+ var normalName = string.IsNullOrEmpty(animationTriggers.normalTrigger) ? "Normal" : animationTriggers.normalTrigger;
+ var highlightedName = string.IsNullOrEmpty(animationTriggers.highlightedTrigger) ? "Highlighted" : animationTriggers.highlightedTrigger;
+ var pressedName = string.IsNullOrEmpty(animationTriggers.pressedTrigger) ? "Pressed" : animationTriggers.pressedTrigger;
+ var disabledName = string.IsNullOrEmpty(animationTriggers.disabledTrigger) ? "Disabled" : animationTriggers.disabledTrigger;
+
+ // Create controller and hook up transitions.
+ var controller = Animations.AnimatorController.CreateAnimatorControllerAtPath(path);
+ GenerateTriggerableTransition(normalName, controller);
+ GenerateTriggerableTransition(highlightedName, controller);
+ GenerateTriggerableTransition(pressedName, controller);
+ GenerateTriggerableTransition(disabledName, controller);
+
+ AssetDatabase.ImportAsset(path);
+
+ return controller;
+ }
+
+ private static string GetSaveControllerPath(Selectable target)
+ {
+ var defaultName = target.gameObject.name;
+ var message = string.Format("Create a new animator for the game object '{0}':", defaultName);
+ return EditorUtility.SaveFilePanelInProject("New Animation Contoller", defaultName, "controller", message);
+ }
+
+ private static void SetUpCurves(AnimationClip highlightedClip, AnimationClip pressedClip, string animationPath)
+ {
+ string[] channels = { "m_LocalScale.x", "m_LocalScale.y", "m_LocalScale.z" };
+
+ var highlightedKeys = new[] { new Keyframe(0f, 1f), new Keyframe(0.5f, 1.1f), new Keyframe(1f, 1f) };
+ var highlightedCurve = new AnimationCurve(highlightedKeys);
+ foreach (var channel in channels)
+ AnimationUtility.SetEditorCurve(highlightedClip, EditorCurveBinding.FloatCurve(animationPath, typeof(Transform), channel), highlightedCurve);
+
+ var pressedKeys = new[] { new Keyframe(0f, 1.15f) };
+ var pressedCurve = new AnimationCurve(pressedKeys);
+ foreach (var channel in channels)
+ AnimationUtility.SetEditorCurve(pressedClip, EditorCurveBinding.FloatCurve(animationPath, typeof(Transform), channel), pressedCurve);
+ }
+
+ private static string BuildAnimationPath(Selectable target)
+ {
+ // if no target don't hook up any curves.
+ var highlight = target.targetGraphic;
+ if (highlight == null)
+ return string.Empty;
+
+ var startGo = highlight.gameObject;
+ var toFindGo = target.gameObject;
+
+ var pathComponents = new Stack<string>();
+ while (toFindGo != startGo)
+ {
+ pathComponents.Push(startGo.name);
+
+ // didn't exist in hierarchy!
+ if (startGo.transform.parent == null)
+ return string.Empty;
+
+ startGo = startGo.transform.parent.gameObject;
+ }
+
+ // calculate path
+ var animPath = new StringBuilder();
+ if (pathComponents.Count > 0)
+ animPath.Append(pathComponents.Pop());
+
+ while (pathComponents.Count > 0)
+ animPath.Append("/").Append(pathComponents.Pop());
+
+ return animPath.ToString();
+ }
+
+ private static AnimationClip GenerateTriggerableTransition(string name, Animations.AnimatorController controller)
+ {
+ // Create the clip
+ var clip = Animations.AnimatorController.AllocateAnimatorClip(name);
+ AssetDatabase.AddObjectToAsset(clip, controller);
+
+ // Create a state in the animatior controller for this clip
+ var state = controller.AddMotion(clip);
+
+ // Add a transition property
+ controller.AddParameter(name, AnimatorControllerParameterType.Trigger);
+
+ // Add an any state transition
+ var stateMachine = controller.layers[0].stateMachine;
+ var transition = stateMachine.AddAnyStateTransition(state);
+ transition.AddCondition(Animations.AnimatorConditionMode.If, 0, name);
+ return clip;
+ }
+
+ private static void StaticOnSceneGUI(SceneView view)
+ {
+ if (!s_ShowNavigation)
+ return;
+
+ for (int i = 0; i < Selectable.allSelectables.Count; i++)
+ {
+ DrawNavigationForSelectable(Selectable.allSelectables[i]);
+ }
+ }
+
+ private static void DrawNavigationForSelectable(Selectable sel)
+ {
+ if (sel == null)
+ return;
+
+ Transform transform = sel.transform;
+ bool active = Selection.transforms.Any(e => e == transform);
+ Handles.color = new Color(1.0f, 0.9f, 0.1f, active ? 1.0f : 0.4f);
+ DrawNavigationArrow(-Vector2.right, sel, sel.FindSelectableOnLeft());
+ DrawNavigationArrow(Vector2.right, sel, sel.FindSelectableOnRight());
+ DrawNavigationArrow(Vector2.up, sel, sel.FindSelectableOnUp());
+ DrawNavigationArrow(-Vector2.up, sel, sel.FindSelectableOnDown());
+ }
+
+ const float kArrowThickness = 2.5f;
+ const float kArrowHeadSize = 1.2f;
+
+ private static void DrawNavigationArrow(Vector2 direction, Selectable fromObj, Selectable toObj)
+ {
+ if (fromObj == null || toObj == null)
+ return;
+ Transform fromTransform = fromObj.transform;
+ Transform toTransform = toObj.transform;
+
+ Vector2 sideDir = new Vector2(direction.y, -direction.x);
+ Vector3 fromPoint = fromTransform.TransformPoint(GetPointOnRectEdge(fromTransform as RectTransform, direction));
+ Vector3 toPoint = toTransform.TransformPoint(GetPointOnRectEdge(toTransform as RectTransform, -direction));
+ float fromSize = HandleUtility.GetHandleSize(fromPoint) * 0.05f;
+ float toSize = HandleUtility.GetHandleSize(toPoint) * 0.05f;
+ fromPoint += fromTransform.TransformDirection(sideDir) * fromSize;
+ toPoint += toTransform.TransformDirection(sideDir) * toSize;
+ float length = Vector3.Distance(fromPoint, toPoint);
+ Vector3 fromTangent = fromTransform.rotation * direction * length * 0.3f;
+ Vector3 toTangent = toTransform.rotation * -direction * length * 0.3f;
+
+ Handles.DrawBezier(fromPoint, toPoint, fromPoint + fromTangent, toPoint + toTangent, Handles.color, null, kArrowThickness);
+ Handles.DrawAAPolyLine(kArrowThickness, toPoint, toPoint + toTransform.rotation * (-direction - sideDir) * toSize * kArrowHeadSize);
+ Handles.DrawAAPolyLine(kArrowThickness, toPoint, toPoint + toTransform.rotation * (-direction + sideDir) * toSize * kArrowHeadSize);
+ }
+
+ private static Vector3 GetPointOnRectEdge(RectTransform rect, Vector2 dir)
+ {
+ if (rect == null)
+ return Vector3.zero;
+ if (dir != Vector2.zero)
+ dir /= Mathf.Max(Mathf.Abs(dir.x), Mathf.Abs(dir.y));
+ dir = rect.rect.center + Vector2.Scale(rect.rect.size, dir * 0.5f);
+ return dir;
+ }
+ }
+}