/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineInspectorUtility.cs
C# | 376 lines | 287 code | 56 blank | 33 comment | 69 complexity | 5c651c739e8e30e9fc6141c140f23f82 MD5 | raw file
- /******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated January 1, 2020. Replaces all prior versions.
- *
- * Copyright (c) 2013-2020, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software
- * or otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
- * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
- using UnityEngine;
- using UnityEditor;
- using System.Collections.Generic;
- using System.Reflection;
- namespace Spine.Unity.Editor {
- public static class SpineInspectorUtility {
- public static string Pluralize (int n, string singular, string plural) {
- return n + " " + (n == 1 ? singular : plural);
- }
- public static string PluralThenS (int n) {
- return n == 1 ? "" : "s";
- }
- public static string EmDash {
- get { return "\u2014"; }
- }
- static GUIContent tempContent;
- internal static GUIContent TempContent (string text, Texture2D image = null, string tooltip = null) {
- if (tempContent == null) tempContent = new GUIContent();
- tempContent.text = text;
- tempContent.image = image;
- tempContent.tooltip = tooltip;
- return tempContent;
- }
- public static void PropertyFieldWideLabel (SerializedProperty property, GUIContent label = null, float minimumLabelWidth = 150) {
- EditorGUIUtility.labelWidth = minimumLabelWidth;
- EditorGUILayout.PropertyField(property, label ?? TempContent(property.displayName, null, property.tooltip));
- EditorGUIUtility.labelWidth = 0; // Resets to default
- }
- public static void PropertyFieldFitLabel (SerializedProperty property, GUIContent label = null, float extraSpace = 5f) {
- label = label ?? TempContent(property.displayName, null, property.tooltip);
- float width = GUI.skin.label.CalcSize(TempContent(label.text)).x + extraSpace;
- if (label.image != null)
- width += EditorGUIUtility.singleLineHeight;
- PropertyFieldWideLabel(property, label, width);
- }
- /// <summary>Multi-edit-compatible version of EditorGUILayout.ToggleLeft(SerializedProperty)</summary>
- public static void ToggleLeftLayout (SerializedProperty property, GUIContent label = null, float width = 120f) {
- if (label == null) label = SpineInspectorUtility.TempContent(property.displayName, tooltip: property.tooltip);
- if (property.hasMultipleDifferentValues) {
- bool previousShowMixedValue = EditorGUI.showMixedValue;
- EditorGUI.showMixedValue = true;
- bool clicked = EditorGUILayout.ToggleLeft(label, property.boolValue, GUILayout.Width(width));
- if (clicked) property.boolValue = true; // Set all values to true when clicked.
- EditorGUI.showMixedValue = previousShowMixedValue;
- } else {
- property.boolValue = EditorGUILayout.ToggleLeft(label, property.boolValue, GUILayout.Width(width));
- }
- }
- /// <summary>Multi-edit-compatible version of EditorGUILayout.ToggleLeft(SerializedProperty)</summary>
- public static void ToggleLeft (Rect rect, SerializedProperty property, GUIContent label = null) {
- if (label == null) label = SpineInspectorUtility.TempContent(property.displayName, tooltip: property.tooltip);
- if (property.hasMultipleDifferentValues) {
- bool previousShowMixedValue = EditorGUI.showMixedValue;
- EditorGUI.showMixedValue = true;
- bool clicked = EditorGUI.ToggleLeft(rect, label, property.boolValue);
- if (clicked) property.boolValue = true; // Set all values to true when clicked.
- EditorGUI.showMixedValue = previousShowMixedValue;
- } else {
- property.boolValue = EditorGUI.ToggleLeft(rect, label, property.boolValue);
- }
- }
- public static bool UndoRedoPerformed (UnityEngine.Event current) {
- return current.type == EventType.ValidateCommand && current.commandName == "UndoRedoPerformed";
- }
- public static Texture2D UnityIcon<T>() {
- return EditorGUIUtility.ObjectContent(null, typeof(T)).image as Texture2D;
- }
- public static Texture2D UnityIcon(System.Type type) {
- return EditorGUIUtility.ObjectContent(null, type).image as Texture2D;
- }
- public static FieldInfo GetNonPublicField (System.Type type, string fieldName) {
- return type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
- }
- #region SerializedProperty Helpers
- public static SerializedProperty FindBaseOrSiblingProperty (this SerializedProperty property, string propertyName) {
- if (string.IsNullOrEmpty(propertyName)) return null;
- SerializedProperty relativeProperty = property.serializedObject.FindProperty(propertyName); // baseProperty
- // If base property is not found, look for the sibling property.
- if (relativeProperty == null) {
- string propertyPath = property.propertyPath;
- int localPathLength = property.name.Length;
- string newPropertyPath = propertyPath.Remove(propertyPath.Length - localPathLength, localPathLength) + propertyName;
- relativeProperty = property.serializedObject.FindProperty(newPropertyPath);
- // If a direct sibling property was not found, try to find the sibling of the array.
- if (relativeProperty == null && property.isArray) {
- int propertyPathLength = propertyPath.Length;
- int dotCount = 0;
- const int SiblingOfListDotCount = 3;
- for (int i = 1; i < propertyPathLength; i++) {
- if (propertyPath[propertyPathLength - i] == '.') {
- dotCount++;
- if (dotCount >= SiblingOfListDotCount) {
- localPathLength = i - 1;
- break;
- }
- }
- }
- newPropertyPath = propertyPath.Remove(propertyPath.Length - localPathLength, localPathLength) + propertyName;
- relativeProperty = property.serializedObject.FindProperty(newPropertyPath);
- }
- }
- return relativeProperty;
- }
- #endregion
- #region Layout Scopes
- static GUIStyle grayMiniLabel;
- public static GUIStyle GrayMiniLabel {
- get {
- if (grayMiniLabel == null) {
- grayMiniLabel = new GUIStyle(EditorStyles.centeredGreyMiniLabel) {
- alignment = TextAnchor.UpperLeft
- };
- }
- return grayMiniLabel;
- }
- }
- public class LabelWidthScope : System.IDisposable {
- public LabelWidthScope (float minimumLabelWidth = 190f) {
- EditorGUIUtility.labelWidth = minimumLabelWidth;
- }
- public void Dispose () {
- EditorGUIUtility.labelWidth = 0f;
- }
- }
- public class IndentScope : System.IDisposable {
- public IndentScope () { EditorGUI.indentLevel++; }
- public void Dispose () { EditorGUI.indentLevel--; }
- }
- public class BoxScope : System.IDisposable {
- readonly bool indent;
- static GUIStyle boxScopeStyle;
- public static GUIStyle BoxScopeStyle {
- get {
- if (boxScopeStyle == null) {
- boxScopeStyle = new GUIStyle(EditorStyles.helpBox);
- RectOffset p = boxScopeStyle.padding; // RectOffset is a class
- p.right += 6;
- p.top += 1;
- p.left += 3;
- }
- return boxScopeStyle;
- }
- }
- public BoxScope (bool indent = true) {
- this.indent = indent;
- EditorGUILayout.BeginVertical(BoxScopeStyle);
- if (indent) EditorGUI.indentLevel++;
- }
- public void Dispose () {
- if (indent) EditorGUI.indentLevel--;
- EditorGUILayout.EndVertical();
- }
- }
- #endregion
- #region Button
- const float CenterButtonMaxWidth = 270f;
- const float CenterButtonHeight = 30f;
- static GUIStyle spineButtonStyle;
- static GUIStyle SpineButtonStyle {
- get {
- if (spineButtonStyle == null) {
- spineButtonStyle = new GUIStyle(GUI.skin.button);
- spineButtonStyle.padding = new RectOffset(10, 10, 10, 10);
- }
- return spineButtonStyle;
- }
- }
- public static bool LargeCenteredButton (string label, bool sideSpace = true, float maxWidth = CenterButtonMaxWidth) {
- if (sideSpace) {
- bool clicked;
- using (new EditorGUILayout.HorizontalScope()) {
- EditorGUILayout.Space();
- clicked = GUILayout.Button(label, SpineButtonStyle, GUILayout.MaxWidth(maxWidth), GUILayout.Height(CenterButtonHeight));
- EditorGUILayout.Space();
- }
- EditorGUILayout.Space();
- return clicked;
- } else {
- return GUILayout.Button(label, GUILayout.MaxWidth(CenterButtonMaxWidth), GUILayout.Height(CenterButtonHeight));
- }
- }
- public static bool LargeCenteredButton (GUIContent content, bool sideSpace = true, float maxWidth = CenterButtonMaxWidth) {
- if (sideSpace) {
- bool clicked;
- using (new EditorGUILayout.HorizontalScope()) {
- EditorGUILayout.Space();
- clicked = GUILayout.Button(content, SpineButtonStyle, GUILayout.MaxWidth(maxWidth), GUILayout.Height(CenterButtonHeight));
- EditorGUILayout.Space();
- }
- EditorGUILayout.Space();
- return clicked;
- } else {
- return GUILayout.Button(content, GUILayout.MaxWidth(CenterButtonMaxWidth), GUILayout.Height(CenterButtonHeight));
- }
- }
- public static bool CenteredButton (GUIContent content, float height = 20f, bool sideSpace = true, float maxWidth = CenterButtonMaxWidth) {
- if (sideSpace) {
- bool clicked;
- using (new EditorGUILayout.HorizontalScope()) {
- EditorGUILayout.Space();
- clicked = GUILayout.Button(content, GUILayout.MaxWidth(maxWidth), GUILayout.Height(height));
- EditorGUILayout.Space();
- }
- EditorGUILayout.Space();
- return clicked;
- } else {
- return GUILayout.Button(content, GUILayout.MaxWidth(maxWidth), GUILayout.Height(height));
- }
- }
- #endregion
- #region Multi-Editing Helpers
- public static bool TargetsUseSameData (SerializedObject so) {
- if (so.isEditingMultipleObjects) {
- int n = so.targetObjects.Length;
- var first = so.targetObjects[0] as IHasSkeletonDataAsset;
- for (int i = 1; i < n; i++) {
- var sr = so.targetObjects[i] as IHasSkeletonDataAsset;
- if (sr != null && sr.SkeletonDataAsset != first.SkeletonDataAsset)
- return false;
- }
- }
- return true;
- }
- public static SerializedObject GetRenderersSerializedObject (SerializedObject serializedObject) {
- if (serializedObject.isEditingMultipleObjects) {
- var renderers = new List<Object>();
- foreach (var o in serializedObject.targetObjects) {
- var component = o as Component;
- if (component != null) {
- var renderer = component.GetComponent<Renderer>();
- if (renderer != null)
- renderers.Add(renderer);
- }
- }
- return new SerializedObject(renderers.ToArray());
- } else {
- var component = serializedObject.targetObject as Component;
- if (component != null) {
- var renderer = component.GetComponent<Renderer>();
- if (renderer != null)
- return new SerializedObject(renderer);
- }
- }
- return null;
- }
- #endregion
- #region Sorting Layer Field Helpers
- static readonly GUIContent SortingLayerLabel = new GUIContent("Sorting Layer", "MeshRenderer.sortingLayerID");
- static readonly GUIContent OrderInLayerLabel = new GUIContent("Order in Layer", "MeshRenderer.sortingOrder");
- static MethodInfo m_SortingLayerFieldMethod;
- static MethodInfo SortingLayerFieldMethod {
- get {
- if (m_SortingLayerFieldMethod == null)
- m_SortingLayerFieldMethod = typeof(EditorGUILayout).GetMethod("SortingLayerField", BindingFlags.Static | BindingFlags.NonPublic, null, new [] { typeof(GUIContent), typeof(SerializedProperty), typeof(GUIStyle) }, null);
- return m_SortingLayerFieldMethod;
- }
- }
- public struct SerializedSortingProperties {
- public SerializedObject renderer;
- public SerializedProperty sortingLayerID;
- public SerializedProperty sortingOrder;
- public SerializedSortingProperties (Renderer r) : this(new SerializedObject(r)) {}
- public SerializedSortingProperties (Object[] renderers) : this(new SerializedObject(renderers)) {}
- public SerializedSortingProperties (SerializedObject rendererSerializedObject) {
- renderer = rendererSerializedObject;
- sortingLayerID = renderer.FindProperty("m_SortingLayerID");
- sortingOrder = renderer.FindProperty("m_SortingOrder");
- }
- public void ApplyModifiedProperties () {
- renderer.ApplyModifiedProperties();
- // SetDirty
- if (renderer.isEditingMultipleObjects)
- foreach (var o in renderer.targetObjects)
- EditorUtility.SetDirty(o);
- else
- EditorUtility.SetDirty(renderer.targetObject);
- }
- }
- public static void SortingPropertyFields (SerializedSortingProperties prop, bool applyModifiedProperties) {
- if (applyModifiedProperties)
- EditorGUI.BeginChangeCheck();
- if (SpineInspectorUtility.SortingLayerFieldMethod != null && prop.sortingLayerID != null)
- SpineInspectorUtility.SortingLayerFieldMethod.Invoke(null, new object[] { SortingLayerLabel, prop.sortingLayerID, EditorStyles.popup } );
- else
- EditorGUILayout.PropertyField(prop.sortingLayerID);
- EditorGUILayout.PropertyField(prop.sortingOrder, OrderInLayerLabel);
- if (applyModifiedProperties && EditorGUI.EndChangeCheck())
- prop.ApplyModifiedProperties();
- }
- #endregion
- }
- }