PageRenderTime 656ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 0ms

/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineInspectorUtility.cs

https://gitlab.com/hoangduy09100/falling-box
C# | 376 lines | 287 code | 56 blank | 33 comment | 69 complexity | 5c651c739e8e30e9fc6141c140f23f82 MD5 | raw file
  1. /******************************************************************************
  2. * Spine Runtimes License Agreement
  3. * Last updated January 1, 2020. Replaces all prior versions.
  4. *
  5. * Copyright (c) 2013-2020, Esoteric Software LLC
  6. *
  7. * Integration of the Spine Runtimes into software or otherwise creating
  8. * derivative works of the Spine Runtimes is permitted under the terms and
  9. * conditions of Section 2 of the Spine Editor License Agreement:
  10. * http://esotericsoftware.com/spine-editor-license
  11. *
  12. * Otherwise, it is permitted to integrate the Spine Runtimes into software
  13. * or otherwise create derivative works of the Spine Runtimes (collectively,
  14. * "Products"), provided that each user of the Products must obtain their own
  15. * Spine Editor license and redistribution of the Products in any form must
  16. * include this license and copyright notice.
  17. *
  18. * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
  19. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
  22. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
  24. * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
  25. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  27. * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. *****************************************************************************/
  29. using UnityEngine;
  30. using UnityEditor;
  31. using System.Collections.Generic;
  32. using System.Reflection;
  33. namespace Spine.Unity.Editor {
  34. public static class SpineInspectorUtility {
  35. public static string Pluralize (int n, string singular, string plural) {
  36. return n + " " + (n == 1 ? singular : plural);
  37. }
  38. public static string PluralThenS (int n) {
  39. return n == 1 ? "" : "s";
  40. }
  41. public static string EmDash {
  42. get { return "\u2014"; }
  43. }
  44. static GUIContent tempContent;
  45. internal static GUIContent TempContent (string text, Texture2D image = null, string tooltip = null) {
  46. if (tempContent == null) tempContent = new GUIContent();
  47. tempContent.text = text;
  48. tempContent.image = image;
  49. tempContent.tooltip = tooltip;
  50. return tempContent;
  51. }
  52. public static void PropertyFieldWideLabel (SerializedProperty property, GUIContent label = null, float minimumLabelWidth = 150) {
  53. EditorGUIUtility.labelWidth = minimumLabelWidth;
  54. EditorGUILayout.PropertyField(property, label ?? TempContent(property.displayName, null, property.tooltip));
  55. EditorGUIUtility.labelWidth = 0; // Resets to default
  56. }
  57. public static void PropertyFieldFitLabel (SerializedProperty property, GUIContent label = null, float extraSpace = 5f) {
  58. label = label ?? TempContent(property.displayName, null, property.tooltip);
  59. float width = GUI.skin.label.CalcSize(TempContent(label.text)).x + extraSpace;
  60. if (label.image != null)
  61. width += EditorGUIUtility.singleLineHeight;
  62. PropertyFieldWideLabel(property, label, width);
  63. }
  64. /// <summary>Multi-edit-compatible version of EditorGUILayout.ToggleLeft(SerializedProperty)</summary>
  65. public static void ToggleLeftLayout (SerializedProperty property, GUIContent label = null, float width = 120f) {
  66. if (label == null) label = SpineInspectorUtility.TempContent(property.displayName, tooltip: property.tooltip);
  67. if (property.hasMultipleDifferentValues) {
  68. bool previousShowMixedValue = EditorGUI.showMixedValue;
  69. EditorGUI.showMixedValue = true;
  70. bool clicked = EditorGUILayout.ToggleLeft(label, property.boolValue, GUILayout.Width(width));
  71. if (clicked) property.boolValue = true; // Set all values to true when clicked.
  72. EditorGUI.showMixedValue = previousShowMixedValue;
  73. } else {
  74. property.boolValue = EditorGUILayout.ToggleLeft(label, property.boolValue, GUILayout.Width(width));
  75. }
  76. }
  77. /// <summary>Multi-edit-compatible version of EditorGUILayout.ToggleLeft(SerializedProperty)</summary>
  78. public static void ToggleLeft (Rect rect, SerializedProperty property, GUIContent label = null) {
  79. if (label == null) label = SpineInspectorUtility.TempContent(property.displayName, tooltip: property.tooltip);
  80. if (property.hasMultipleDifferentValues) {
  81. bool previousShowMixedValue = EditorGUI.showMixedValue;
  82. EditorGUI.showMixedValue = true;
  83. bool clicked = EditorGUI.ToggleLeft(rect, label, property.boolValue);
  84. if (clicked) property.boolValue = true; // Set all values to true when clicked.
  85. EditorGUI.showMixedValue = previousShowMixedValue;
  86. } else {
  87. property.boolValue = EditorGUI.ToggleLeft(rect, label, property.boolValue);
  88. }
  89. }
  90. public static bool UndoRedoPerformed (UnityEngine.Event current) {
  91. return current.type == EventType.ValidateCommand && current.commandName == "UndoRedoPerformed";
  92. }
  93. public static Texture2D UnityIcon<T>() {
  94. return EditorGUIUtility.ObjectContent(null, typeof(T)).image as Texture2D;
  95. }
  96. public static Texture2D UnityIcon(System.Type type) {
  97. return EditorGUIUtility.ObjectContent(null, type).image as Texture2D;
  98. }
  99. public static FieldInfo GetNonPublicField (System.Type type, string fieldName) {
  100. return type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
  101. }
  102. #region SerializedProperty Helpers
  103. public static SerializedProperty FindBaseOrSiblingProperty (this SerializedProperty property, string propertyName) {
  104. if (string.IsNullOrEmpty(propertyName)) return null;
  105. SerializedProperty relativeProperty = property.serializedObject.FindProperty(propertyName); // baseProperty
  106. // If base property is not found, look for the sibling property.
  107. if (relativeProperty == null) {
  108. string propertyPath = property.propertyPath;
  109. int localPathLength = property.name.Length;
  110. string newPropertyPath = propertyPath.Remove(propertyPath.Length - localPathLength, localPathLength) + propertyName;
  111. relativeProperty = property.serializedObject.FindProperty(newPropertyPath);
  112. // If a direct sibling property was not found, try to find the sibling of the array.
  113. if (relativeProperty == null && property.isArray) {
  114. int propertyPathLength = propertyPath.Length;
  115. int dotCount = 0;
  116. const int SiblingOfListDotCount = 3;
  117. for (int i = 1; i < propertyPathLength; i++) {
  118. if (propertyPath[propertyPathLength - i] == '.') {
  119. dotCount++;
  120. if (dotCount >= SiblingOfListDotCount) {
  121. localPathLength = i - 1;
  122. break;
  123. }
  124. }
  125. }
  126. newPropertyPath = propertyPath.Remove(propertyPath.Length - localPathLength, localPathLength) + propertyName;
  127. relativeProperty = property.serializedObject.FindProperty(newPropertyPath);
  128. }
  129. }
  130. return relativeProperty;
  131. }
  132. #endregion
  133. #region Layout Scopes
  134. static GUIStyle grayMiniLabel;
  135. public static GUIStyle GrayMiniLabel {
  136. get {
  137. if (grayMiniLabel == null) {
  138. grayMiniLabel = new GUIStyle(EditorStyles.centeredGreyMiniLabel) {
  139. alignment = TextAnchor.UpperLeft
  140. };
  141. }
  142. return grayMiniLabel;
  143. }
  144. }
  145. public class LabelWidthScope : System.IDisposable {
  146. public LabelWidthScope (float minimumLabelWidth = 190f) {
  147. EditorGUIUtility.labelWidth = minimumLabelWidth;
  148. }
  149. public void Dispose () {
  150. EditorGUIUtility.labelWidth = 0f;
  151. }
  152. }
  153. public class IndentScope : System.IDisposable {
  154. public IndentScope () { EditorGUI.indentLevel++; }
  155. public void Dispose () { EditorGUI.indentLevel--; }
  156. }
  157. public class BoxScope : System.IDisposable {
  158. readonly bool indent;
  159. static GUIStyle boxScopeStyle;
  160. public static GUIStyle BoxScopeStyle {
  161. get {
  162. if (boxScopeStyle == null) {
  163. boxScopeStyle = new GUIStyle(EditorStyles.helpBox);
  164. RectOffset p = boxScopeStyle.padding; // RectOffset is a class
  165. p.right += 6;
  166. p.top += 1;
  167. p.left += 3;
  168. }
  169. return boxScopeStyle;
  170. }
  171. }
  172. public BoxScope (bool indent = true) {
  173. this.indent = indent;
  174. EditorGUILayout.BeginVertical(BoxScopeStyle);
  175. if (indent) EditorGUI.indentLevel++;
  176. }
  177. public void Dispose () {
  178. if (indent) EditorGUI.indentLevel--;
  179. EditorGUILayout.EndVertical();
  180. }
  181. }
  182. #endregion
  183. #region Button
  184. const float CenterButtonMaxWidth = 270f;
  185. const float CenterButtonHeight = 30f;
  186. static GUIStyle spineButtonStyle;
  187. static GUIStyle SpineButtonStyle {
  188. get {
  189. if (spineButtonStyle == null) {
  190. spineButtonStyle = new GUIStyle(GUI.skin.button);
  191. spineButtonStyle.padding = new RectOffset(10, 10, 10, 10);
  192. }
  193. return spineButtonStyle;
  194. }
  195. }
  196. public static bool LargeCenteredButton (string label, bool sideSpace = true, float maxWidth = CenterButtonMaxWidth) {
  197. if (sideSpace) {
  198. bool clicked;
  199. using (new EditorGUILayout.HorizontalScope()) {
  200. EditorGUILayout.Space();
  201. clicked = GUILayout.Button(label, SpineButtonStyle, GUILayout.MaxWidth(maxWidth), GUILayout.Height(CenterButtonHeight));
  202. EditorGUILayout.Space();
  203. }
  204. EditorGUILayout.Space();
  205. return clicked;
  206. } else {
  207. return GUILayout.Button(label, GUILayout.MaxWidth(CenterButtonMaxWidth), GUILayout.Height(CenterButtonHeight));
  208. }
  209. }
  210. public static bool LargeCenteredButton (GUIContent content, bool sideSpace = true, float maxWidth = CenterButtonMaxWidth) {
  211. if (sideSpace) {
  212. bool clicked;
  213. using (new EditorGUILayout.HorizontalScope()) {
  214. EditorGUILayout.Space();
  215. clicked = GUILayout.Button(content, SpineButtonStyle, GUILayout.MaxWidth(maxWidth), GUILayout.Height(CenterButtonHeight));
  216. EditorGUILayout.Space();
  217. }
  218. EditorGUILayout.Space();
  219. return clicked;
  220. } else {
  221. return GUILayout.Button(content, GUILayout.MaxWidth(CenterButtonMaxWidth), GUILayout.Height(CenterButtonHeight));
  222. }
  223. }
  224. public static bool CenteredButton (GUIContent content, float height = 20f, bool sideSpace = true, float maxWidth = CenterButtonMaxWidth) {
  225. if (sideSpace) {
  226. bool clicked;
  227. using (new EditorGUILayout.HorizontalScope()) {
  228. EditorGUILayout.Space();
  229. clicked = GUILayout.Button(content, GUILayout.MaxWidth(maxWidth), GUILayout.Height(height));
  230. EditorGUILayout.Space();
  231. }
  232. EditorGUILayout.Space();
  233. return clicked;
  234. } else {
  235. return GUILayout.Button(content, GUILayout.MaxWidth(maxWidth), GUILayout.Height(height));
  236. }
  237. }
  238. #endregion
  239. #region Multi-Editing Helpers
  240. public static bool TargetsUseSameData (SerializedObject so) {
  241. if (so.isEditingMultipleObjects) {
  242. int n = so.targetObjects.Length;
  243. var first = so.targetObjects[0] as IHasSkeletonDataAsset;
  244. for (int i = 1; i < n; i++) {
  245. var sr = so.targetObjects[i] as IHasSkeletonDataAsset;
  246. if (sr != null && sr.SkeletonDataAsset != first.SkeletonDataAsset)
  247. return false;
  248. }
  249. }
  250. return true;
  251. }
  252. public static SerializedObject GetRenderersSerializedObject (SerializedObject serializedObject) {
  253. if (serializedObject.isEditingMultipleObjects) {
  254. var renderers = new List<Object>();
  255. foreach (var o in serializedObject.targetObjects) {
  256. var component = o as Component;
  257. if (component != null) {
  258. var renderer = component.GetComponent<Renderer>();
  259. if (renderer != null)
  260. renderers.Add(renderer);
  261. }
  262. }
  263. return new SerializedObject(renderers.ToArray());
  264. } else {
  265. var component = serializedObject.targetObject as Component;
  266. if (component != null) {
  267. var renderer = component.GetComponent<Renderer>();
  268. if (renderer != null)
  269. return new SerializedObject(renderer);
  270. }
  271. }
  272. return null;
  273. }
  274. #endregion
  275. #region Sorting Layer Field Helpers
  276. static readonly GUIContent SortingLayerLabel = new GUIContent("Sorting Layer", "MeshRenderer.sortingLayerID");
  277. static readonly GUIContent OrderInLayerLabel = new GUIContent("Order in Layer", "MeshRenderer.sortingOrder");
  278. static MethodInfo m_SortingLayerFieldMethod;
  279. static MethodInfo SortingLayerFieldMethod {
  280. get {
  281. if (m_SortingLayerFieldMethod == null)
  282. m_SortingLayerFieldMethod = typeof(EditorGUILayout).GetMethod("SortingLayerField", BindingFlags.Static | BindingFlags.NonPublic, null, new [] { typeof(GUIContent), typeof(SerializedProperty), typeof(GUIStyle) }, null);
  283. return m_SortingLayerFieldMethod;
  284. }
  285. }
  286. public struct SerializedSortingProperties {
  287. public SerializedObject renderer;
  288. public SerializedProperty sortingLayerID;
  289. public SerializedProperty sortingOrder;
  290. public SerializedSortingProperties (Renderer r) : this(new SerializedObject(r)) {}
  291. public SerializedSortingProperties (Object[] renderers) : this(new SerializedObject(renderers)) {}
  292. public SerializedSortingProperties (SerializedObject rendererSerializedObject) {
  293. renderer = rendererSerializedObject;
  294. sortingLayerID = renderer.FindProperty("m_SortingLayerID");
  295. sortingOrder = renderer.FindProperty("m_SortingOrder");
  296. }
  297. public void ApplyModifiedProperties () {
  298. renderer.ApplyModifiedProperties();
  299. // SetDirty
  300. if (renderer.isEditingMultipleObjects)
  301. foreach (var o in renderer.targetObjects)
  302. EditorUtility.SetDirty(o);
  303. else
  304. EditorUtility.SetDirty(renderer.targetObject);
  305. }
  306. }
  307. public static void SortingPropertyFields (SerializedSortingProperties prop, bool applyModifiedProperties) {
  308. if (applyModifiedProperties)
  309. EditorGUI.BeginChangeCheck();
  310. if (SpineInspectorUtility.SortingLayerFieldMethod != null && prop.sortingLayerID != null)
  311. SpineInspectorUtility.SortingLayerFieldMethod.Invoke(null, new object[] { SortingLayerLabel, prop.sortingLayerID, EditorStyles.popup } );
  312. else
  313. EditorGUILayout.PropertyField(prop.sortingLayerID);
  314. EditorGUILayout.PropertyField(prop.sortingOrder, OrderInLayerLabel);
  315. if (applyModifiedProperties && EditorGUI.EndChangeCheck())
  316. prop.ApplyModifiedProperties();
  317. }
  318. #endregion
  319. }
  320. }