Browse Source

[unity] More SkeletonDebugWindow, improved inspectors and icons for SpineAttributeDrawers!

pharan 8 years ago
parent
commit
a43bdc4361
18 changed files with 438 additions and 118 deletions
  1. 2 2
      spine-unity/Assets/spine-unity/Asset Types/Editor/AtlasAssetInspector.cs
  2. 20 20
      spine-unity/Assets/spine-unity/Asset Types/Editor/SkeletonDataAssetInspector.cs
  3. 8 10
      spine-unity/Assets/spine-unity/Editor/BoneFollowerInspector.cs
  4. BIN
      spine-unity/Assets/spine-unity/Editor/GUI/icon-attachment.png
  5. 92 0
      spine-unity/Assets/spine-unity/Editor/GUI/icon-attachment.png.meta
  6. 1 0
      spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs
  7. 171 47
      spine-unity/Assets/spine-unity/Editor/SkeletonDebugWindow.cs
  8. 22 13
      spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs
  9. 38 10
      spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs
  10. 33 3
      spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs
  11. 16 4
      spine-unity/Assets/spine-unity/Editor/SpineInspectorUtility.cs
  12. 1 1
      spine-unity/Assets/spine-unity/Modules/CustomMaterials/Editor/SkeletonRendererCustomMaterialsInspector.cs
  13. 2 2
      spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs
  14. 7 0
      spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonPartsRendererInspector.cs
  15. 2 0
      spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonRenderSeparatorInspector.cs
  16. 16 2
      spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonRenderSeparator.cs
  17. 4 4
      spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityBoneInspector.cs
  18. 3 0
      spine-unity/Assets/spine-unity/SpineAttributes.cs

+ 2 - 2
spine-unity/Assets/spine-unity/Asset Types/Editor/AtlasAssetInspector.cs

@@ -120,7 +120,7 @@ namespace Spine.Unity.Editor {
 			}
 
 			if (materials.arraySize == 0) {
-				EditorGUILayout.LabelField(new GUIContent("Error:  Missing materials", SpineEditorUtilities.Icons.warning));
+				EditorGUILayout.HelpBox("Missing materials", MessageType.Error);
 				return;
 			}
 
@@ -128,7 +128,7 @@ namespace Spine.Unity.Editor {
 				SerializedProperty prop = materials.GetArrayElementAtIndex(i);
 				Material mat = (Material)prop.objectReferenceValue;
 				if (mat == null) {
-					EditorGUILayout.LabelField(new GUIContent("Error:  Materials cannot be null", SpineEditorUtilities.Icons.warning));
+					EditorGUILayout.HelpBox("Materials cannot be null.", MessageType.Error);
 					return;
 				}
 			}

+ 20 - 20
spine-unity/Assets/spine-unity/Asset Types/Editor/SkeletonDataAssetInspector.cs

@@ -126,7 +126,7 @@ namespace Spine.Unity.Editor {
 			if (serializedObject.isEditingMultipleObjects) {
 				using (new SpineInspectorUtility.BoxScope()) {
 					EditorGUILayout.LabelField("SkeletonData", EditorStyles.boldLabel);
-					EditorGUILayout.PropertyField(skeletonJSON, new GUIContent(skeletonJSON.displayName, Icons.spine));
+					EditorGUILayout.PropertyField(skeletonJSON, SpineInspectorUtility.TempContent(skeletonJSON.displayName, Icons.spine));
 					EditorGUILayout.PropertyField(scale);
 				}
 
@@ -162,7 +162,7 @@ namespace Spine.Unity.Editor {
 
 			serializedObject.Update();
 
-			EditorGUILayout.LabelField(new GUIContent(target.name + " (SkeletonDataAsset)", Icons.spine), EditorStyles.whiteLargeLabel);
+			EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(target.name + " (SkeletonDataAsset)", Icons.spine), EditorStyles.whiteLargeLabel);
 			if (m_skeletonData != null) {
 				EditorGUILayout.LabelField("(Drag and Drop to instantiate.)", EditorStyles.miniLabel);
 			}
@@ -181,7 +181,7 @@ namespace Spine.Unity.Editor {
 //					}
 				}
 
-				EditorGUILayout.PropertyField(skeletonJSON, new GUIContent(skeletonJSON.displayName, Icons.spine));
+				EditorGUILayout.PropertyField(skeletonJSON, SpineInspectorUtility.TempContent(skeletonJSON.displayName, Icons.spine));
 				EditorGUILayout.PropertyField(scale);
 			}
 
@@ -239,7 +239,7 @@ namespace Spine.Unity.Editor {
 				#if !SPINE_TK2D
 				// Reimport Button
 				using (new EditorGUI.DisabledGroupScope(skeletonJSON.objectReferenceValue == null)) {
-					if (GUILayout.Button(new GUIContent("Attempt Reimport", Icons.warning))) {
+					if (GUILayout.Button(SpineInspectorUtility.TempContent("Attempt Reimport", Icons.warning))) {
 						DoReimport();
 					}
 				}
@@ -249,7 +249,7 @@ namespace Spine.Unity.Editor {
 
 				// List warnings.
 				foreach (var line in warnings)
-					EditorGUILayout.LabelField(new GUIContent(line, Icons.warning));
+					EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(line, Icons.warning));
 			}
 
 			if (!Application.isPlaying)
@@ -259,16 +259,16 @@ namespace Spine.Unity.Editor {
 		void DrawUnityTools () {
 			#if SPINE_SKELETON_ANIMATOR
 			using (new SpineInspectorUtility.BoxScope()) {
-				isMecanimExpanded = EditorGUILayout.Foldout(isMecanimExpanded, new GUIContent("SkeletonAnimator", Icons.unityIcon));
+				isMecanimExpanded = EditorGUILayout.Foldout(isMecanimExpanded, SpineInspectorUtility.TempContent("SkeletonAnimator", Icons.unityIcon));
 				if (isMecanimExpanded) {
 					EditorGUI.indentLevel++;
-					EditorGUILayout.PropertyField(controller, new GUIContent("Controller", Icons.controllerIcon));		
+					EditorGUILayout.PropertyField(controller, SpineInspectorUtility.TempContent("Controller", Icons.controllerIcon));		
 					if (controller.objectReferenceValue == null) {
 
 						// Generate Mecanim Controller Button
 						using (new GUILayout.HorizontalScope()) {
 							GUILayout.Space(EditorGUIUtility.labelWidth);
-							if (GUILayout.Button(new GUIContent("Generate Mecanim Controller"), GUILayout.Height(20)))
+							if (GUILayout.Button(SpineInspectorUtility.TempContent("Generate Mecanim Controller"), GUILayout.Height(20)))
 								SkeletonBaker.GenerateMecanimAnimationClips(m_skeletonDataAsset);						
 						}
 						EditorGUILayout.HelpBox("SkeletonAnimator is the Mecanim alternative to SkeletonAnimation.\nIt is not required.", MessageType.Info);
@@ -278,7 +278,7 @@ namespace Spine.Unity.Editor {
 						// Update AnimationClips button.
 						using (new GUILayout.HorizontalScope()) {
 							GUILayout.Space(EditorGUIUtility.labelWidth);
-							if (GUILayout.Button(new GUIContent("Force Update AnimationClips"), GUILayout.Height(20)))
+							if (GUILayout.Button(SpineInspectorUtility.TempContent("Force Update AnimationClips"), GUILayout.Height(20)))
 								SkeletonBaker.GenerateMecanimAnimationClips(m_skeletonDataAsset);				
 						}
 
@@ -414,12 +414,12 @@ namespace Spine.Unity.Editor {
 		}
 
 		void DrawAnimationList () {
-			showAnimationList = EditorGUILayout.Foldout(showAnimationList, new GUIContent(string.Format("Animations [{0}]", m_skeletonData.Animations.Count), Icons.animationRoot));
+			showAnimationList = EditorGUILayout.Foldout(showAnimationList, SpineInspectorUtility.TempContent(string.Format("Animations [{0}]", m_skeletonData.Animations.Count), Icons.animationRoot));
 			if (!showAnimationList)
 				return;
 
 			if (m_skeletonAnimation != null && m_skeletonAnimation.state != null) {
-				if (GUILayout.Button(new GUIContent("Setup Pose", Icons.skeleton), GUILayout.Width(105), GUILayout.Height(18))) {
+				if (GUILayout.Button(SpineInspectorUtility.TempContent("Setup Pose", Icons.skeleton), GUILayout.Width(105), GUILayout.Height(18))) {
 					StopAnimation();
 					m_skeletonAnimation.skeleton.SetToSetupPose();
 					m_requireRefresh = true;
@@ -428,7 +428,7 @@ namespace Spine.Unity.Editor {
 				EditorGUILayout.HelpBox("Animations can be previewed if you expand the Preview window below.", MessageType.Info);
 			}
 
-			EditorGUILayout.LabelField("Name", "Duration");
+			EditorGUILayout.LabelField("Name", "      Duration");
 			foreach (Spine.Animation animation in m_skeletonData.Animations) {
 				using (new GUILayout.HorizontalScope()) {
 					if (m_skeletonAnimation != null && m_skeletonAnimation.state != null) {
@@ -445,13 +445,13 @@ namespace Spine.Unity.Editor {
 					} else {
 						GUILayout.Label("-", GUILayout.Width(24));
 					}
-					EditorGUILayout.LabelField(new GUIContent(animation.Name, Icons.animation), new GUIContent(animation.Duration.ToString("f3") + "s" + ("(" + (Mathf.RoundToInt(animation.Duration * 30)) + ")").PadLeft(12, ' ')));
+					EditorGUILayout.LabelField(new GUIContent(animation.Name, Icons.animation), SpineInspectorUtility.TempContent(animation.Duration.ToString("f3") + "s" + ("(" + (Mathf.RoundToInt(animation.Duration * 30)) + ")").PadLeft(12, ' ')));
 				}
 			}
 		}
 
 		void DrawSlotList () {
-			showSlotList = EditorGUILayout.Foldout(showSlotList, new GUIContent("Slots", Icons.slotRoot));
+			showSlotList = EditorGUILayout.Foldout(showSlotList, SpineInspectorUtility.TempContent("Slots", Icons.slotRoot));
 
 			if (!showSlotList) return;
 			if (m_skeletonAnimation == null || m_skeletonAnimation.skeleton == null) return;
@@ -467,7 +467,7 @@ namespace Spine.Unity.Editor {
 
 			for (int i = m_skeletonAnimation.skeleton.Slots.Count - 1; i >= 0; i--) {
 				Slot slot = slotsItems[i];
-				EditorGUILayout.LabelField(new GUIContent(slot.Data.Name, Icons.slot));
+				EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(slot.Data.Name, Icons.slot));
 				if (showAttachments) {
 					
 					EditorGUI.indentLevel++;
@@ -491,7 +491,7 @@ namespace Spine.Unity.Editor {
 						string attachmentName = slotAttachmentNames[a];
 						Texture2D icon = Icons.GetAttachmentIcon(attachment);
 						bool initialState = slot.Attachment == attachment;
-						bool toggled = EditorGUILayout.ToggleLeft(new GUIContent(attachmentName, icon), slot.Attachment == attachment);
+						bool toggled = EditorGUILayout.ToggleLeft(SpineInspectorUtility.TempContent(attachmentName, icon), slot.Attachment == attachment);
 
 						if (!defaultSkinAttachmentNames.Contains(attachmentName)) {
 							Rect skinPlaceHolderIconRect = GUILayoutUtility.GetLastRect();
@@ -831,13 +831,13 @@ namespace Spine.Unity.Editor {
 				popRect.x += 4;
 				popRect.height = 24;
 				popRect.width = 40;
-				EditorGUI.DropShadowLabel(popRect, new GUIContent("Skin", Icons.skinsRoot));
+				EditorGUI.DropShadowLabel(popRect, SpineInspectorUtility.TempContent("Skin"));
 
 				popRect.y += 11;
 				popRect.width = 150;
 				popRect.x += 44;
 
-				if (GUI.Button(popRect, label, EditorStyles.popup)) {
+				if (GUI.Button(popRect, SpineInspectorUtility.TempContent(label, Icons.skin), EditorStyles.popup)) {
 					DrawSkinDropdown();
 				}
 			}
@@ -954,7 +954,7 @@ namespace Spine.Unity.Editor {
 		*/
 
 		public override GUIContent GetPreviewTitle () {
-			return new GUIContent("Preview");
+			return SpineInspectorUtility.TempContent("Preview");
 		}
 
 		public override void OnPreviewSettings () {
@@ -997,7 +997,7 @@ namespace Spine.Unity.Editor {
 		void DrawSkinDropdown () {
 			var menu = new GenericMenu();
 			foreach (Skin s in m_skeletonData.Skins)
-				menu.AddItem(new GUIContent(s.Name), this.m_skeletonAnimation.skeleton.Skin == s, SetSkin, s);
+				menu.AddItem(new GUIContent(s.Name, Icons.skin), this.m_skeletonAnimation.skeleton.Skin == s, SetSkin, s);
 			
 			menu.ShowAsContext();
 		}

+ 8 - 10
spine-unity/Assets/spine-unity/Editor/BoneFollowerInspector.cs

@@ -167,16 +167,14 @@ namespace Spine.Unity.Editor {
 
 				bool hasCollider2D = targetBoneFollower.GetComponent<Collider2D>() != null || targetBoneFollower.GetComponent<BoundingBoxFollower>() != null;
 				bool hasCollider3D = !hasCollider2D && targetBoneFollower.GetComponent<Collider>();
-				if (hasCollider2D || hasCollider3D) {
-					if (targetBoneFollower.GetComponent<Rigidbody2D>() == null) {
-						using (new SpineInspectorUtility.BoxScope()) {
-							EditorGUILayout.HelpBox("Collider detected. Unity recommends adding a Rigidbody to the parent Transforms of any colliders that are intended to be dynamically repositioned and rotated.", MessageType.Warning);
-							string rbLabel = hasCollider2D ? "Add Rigidbody2D" : "Add Rigidbody";
-							var rbType = hasCollider2D ? typeof(Rigidbody2D) : typeof(Rigidbody);
-							var rbContent = new GUIContent(rbLabel, "Add a rigidbody to this GameObject to be the Box2D parent of the attached collider.");
-							rbContent.image = EditorGUIUtility.ObjectContent(null, rbType).image;
-							if (SpineInspectorUtility.CenteredButton(rbContent)) targetBoneFollower.gameObject.AddComponent(rbType);
-						}
+				bool missingRigidBody = (hasCollider2D && targetBoneFollower.GetComponent<Rigidbody2D>() == null) || (hasCollider3D && targetBoneFollower.GetComponent<Rigidbody>() == null);
+				if (missingRigidBody) {
+					using (new SpineInspectorUtility.BoxScope()) {
+						EditorGUILayout.HelpBox("Collider detected. Unity recommends adding a Rigidbody to the parent Transforms of any colliders that are intended to be dynamically repositioned and rotated.", MessageType.Warning);
+						var rbType = hasCollider2D ? typeof(Rigidbody2D) : typeof(Rigidbody);
+						string rbLabel = string.Format("Add {0}", rbType.Name);
+						var rbContent = SpineInspectorUtility.TempContent(rbLabel, SpineInspectorUtility.UnityIcon(rbType), "Add a rigidbody to this GameObject to be the Box2D parent of the attached collider.");
+						if (SpineInspectorUtility.CenteredButton(rbContent)) targetBoneFollower.gameObject.AddComponent(rbType);
 					}
 				}
 			} else {

BIN
spine-unity/Assets/spine-unity/Editor/GUI/icon-attachment.png


+ 92 - 0
spine-unity/Assets/spine-unity/Editor/GUI/icon-attachment.png.meta

@@ -0,0 +1,92 @@
+fileFormatVersion: 2
+guid: 04ae56b3698d3e844844cfcef2f009e7
+timeCreated: 1494928093
+licenseType: Free
+TextureImporter:
+  fileIDToRecycleName: {}
+  serializedVersion: 4
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    sRGBTexture: 0
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    filterMode: -1
+    aniso: -1
+    mipBias: -1
+    wrapMode: 1
+  nPOTScale: 0
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 1
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spritePixelsToUnits: 100
+  alphaUsage: 1
+  alphaIsTransparency: 1
+  spriteTessellationDetail: -1
+  textureType: 2
+  textureShape: 1
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - buildTarget: DefaultTexturePlatform
+    maxTextureSize: 2048
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+  - buildTarget: Standalone
+    maxTextureSize: 2048
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+  - buildTarget: Android
+    maxTextureSize: 2048
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+  - buildTarget: WebGL
+    maxTextureSize: 2048
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+  spritePackingTag: 
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 1 - 0
spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs

@@ -88,6 +88,7 @@ namespace Spine.Unity.Editor {
 				EditorGUILayout.PropertyField(timeScale, TimeScaleLabel);
 				var component = (SkeletonAnimation)target;
 				component.timeScale = Mathf.Max(component.timeScale, 0);
+				EditorGUILayout.Space();
 			}
 
 			if (!isInspectingPrefab) {

+ 171 - 47
spine-unity/Assets/spine-unity/Editor/SkeletonDebugWindow.cs

@@ -42,6 +42,7 @@ namespace Spine.Unity.Editor {
 	public class SkeletonDebugWindow : EditorWindow {
 
 		const bool IsUtilityWindow = true;
+		internal static bool showBoneNames, showPaths = true, showShapes = true, showConstraints = true;
 
 		[MenuItem("CONTEXT/SkeletonRenderer/Open Skeleton Debug Window", false, 5000)]
 		public static void Init () {
@@ -79,7 +80,28 @@ namespace Spine.Unity.Editor {
 
 		readonly Dictionary<Slot, List<Attachment>> attachmentTable = new Dictionary<Slot, List<Attachment>>();
 
+		static bool staticLostValues = true;
+
+		void OnSceneGUI (SceneView sceneView) {
+			if (skeleton == null || skeletonRenderer == null || !skeletonRenderer.valid || isPrefab)
+				return;
+
+			var transform = skeletonRenderer.transform;
+			if (showPaths) SpineHandles.DrawPaths(transform, skeleton);
+			if (showConstraints) SpineHandles.DrawConstraints(transform, skeleton);
+			if (showBoneNames) SpineHandles.DrawBoneNames(transform, skeleton);
+			if (showShapes) SpineHandles.DrawBoundingBoxes(transform, skeleton);
+
+			if (bone != null) {
+				SpineHandles.DrawBone(skeletonRenderer.transform, bone, 1.5f, Color.cyan);
+				Handles.Label(bone.GetWorldPosition(skeletonRenderer.transform) + (Vector3.down * 0.15f), bone.Data.Name, SpineHandles.BoneNameStyle);
+			}			
+		}
+
 		void OnSelectionChange () {
+			SceneView.onSceneGUIDelegate -= this.OnSceneGUI;
+			SceneView.onSceneGUIDelegate += this.OnSceneGUI;
+
 			bool noSkeletonRenderer = false;
 
 			var selectedObject = Selection.activeGameObject;
@@ -90,6 +112,11 @@ namespace Spine.Unity.Editor {
 				if (selectedSkeletonRenderer == null) {
 					noSkeletonRenderer = true;
 				} else if (skeletonRenderer != selectedSkeletonRenderer) {
+					
+					bone = null;
+					if (skeletonRenderer != null && skeletonRenderer.SkeletonDataAsset != selectedSkeletonRenderer.SkeletonDataAsset)
+						boneName = null;
+
 					skeletonRenderer = selectedSkeletonRenderer;
 					skeletonRenderer.Initialize(false);
 					skeletonRenderer.LateUpdate();
@@ -99,19 +126,43 @@ namespace Spine.Unity.Editor {
 				}
 			} 
 
-			if (noSkeletonRenderer) {
-				skeletonRenderer = null;
-				skeleton = null;
-				attachmentTable.Clear();
-				isPrefab = false;
-				boneName = string.Empty;
-				bone = null;
-			}				
-
+			if (noSkeletonRenderer) Clear();
 			Repaint();
 		}
 
+		void Clear () {
+			skeletonRenderer = null;
+			skeleton = null;
+			attachmentTable.Clear();
+			isPrefab = false;
+			boneName = string.Empty;
+			bone = null;
+			SceneView.onSceneGUIDelegate -= this.OnSceneGUI;
+		}
+
+		void OnDestroy () {
+			Clear();
+		}
+
+		static void FalseDropDown (string label, string stringValue, Texture2D icon = null, bool disabledGroup = false) {
+			if (disabledGroup) EditorGUI.BeginDisabledGroup(true);
+			var pos = EditorGUILayout.GetControlRect(true);
+			pos = EditorGUI.PrefixLabel(pos, SpineInspectorUtility.TempContent(label));
+			GUI.Button(pos, SpineInspectorUtility.TempContent(stringValue, icon), EditorStyles.popup);
+			if (disabledGroup) EditorGUI.EndDisabledGroup();
+		}
+
+		// Window GUI
 		void OnGUI () {
+			bool requireRepaint = false;
+
+			if (staticLostValues) {
+				Clear();
+				OnSelectionChange();
+				staticLostValues = false;
+				requireRepaint = true;
+			}
+
 			if (SlotsRootLabel == null) {
 				SlotsRootLabel = new GUIContent("Slots", Icons.slotRoot);
 				SkeletonRootLabel = new GUIContent("Skeleton", Icons.skeleton);
@@ -121,21 +172,24 @@ namespace Spine.Unity.Editor {
 				BoldFoldoutStyle.fixedWidth = 0;
 			}
 
-			bool requireRepaint = false;
+
 			EditorGUILayout.Space();
 			EditorGUI.BeginDisabledGroup(true);
-			EditorGUILayout.ObjectField("Debug Selection", skeletonRenderer, typeof(SkeletonRenderer), true);
+			EditorGUILayout.ObjectField(SpineInspectorUtility.TempContent("Debug Selection", Icons.spine), skeletonRenderer,  typeof(SkeletonRenderer), true);
 			EditorGUI.EndDisabledGroup();
 
-			if (skeleton == null || skeletonRenderer == null || !skeletonRenderer.valid) return;
+			if (skeleton == null || skeletonRenderer == null) {
+				EditorGUILayout.HelpBox("No SkeletonRenderer Spine GameObject selected.", MessageType.Info);
+				return;
+			}
 
 			if (isPrefab) {
-				GUILayout.Label(new GUIContent("Cannot edit Prefabs", Icons.warning));
+				EditorGUILayout.HelpBox("SkeletonDebug only debugs Spine GameObjects in the scene.", MessageType.Warning);
 				return;
 			}
 
 			if (!skeletonRenderer.valid) {
-				GUILayout.Label(new GUIContent("Spine Component invalid. Check Skeleton Data Asset.", Icons.warning));
+				EditorGUILayout.HelpBox("Spine Component is invalid. Check SkeletonData Asset.", MessageType.Error);
 				return;	
 			}
 
@@ -145,7 +199,7 @@ namespace Spine.Unity.Editor {
 			scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
 
 			using (new SpineInspectorUtility.BoxScope(false)) {
-				if (SpineInspectorUtility.CenteredButton(new GUIContent("Skeleton.SetToSetupPose()"))) {
+				if (SpineInspectorUtility.CenteredButton(SpineInspectorUtility.TempContent("Skeleton.SetToSetupPose()"))) {
 					skeleton.SetToSetupPose();
 					requireRepaint = true;
 				}
@@ -153,10 +207,10 @@ namespace Spine.Unity.Editor {
 				EditorGUI.BeginChangeCheck();
 				EditorGUILayout.LabelField("Scene View", EditorStyles.boldLabel);
 				using (new SpineInspectorUtility.LabelWidthScope()) {
-					SkeletonRendererInspector.showBoneNames = EditorGUILayout.Toggle("Show Bone Names", SkeletonRendererInspector.showBoneNames);
-					SkeletonRendererInspector.showPaths = EditorGUILayout.Toggle("Show Paths", SkeletonRendererInspector.showPaths);
-					SkeletonRendererInspector.showShapes = EditorGUILayout.Toggle("Show Shapes", SkeletonRendererInspector.showShapes);
-					SkeletonRendererInspector.showConstraints = EditorGUILayout.Toggle("Show Constraints", SkeletonRendererInspector.showConstraints);
+					showBoneNames = EditorGUILayout.Toggle("Show Bone Names", showBoneNames);
+					showPaths = EditorGUILayout.Toggle("Show Paths", showPaths);
+					showShapes = EditorGUILayout.Toggle("Show Shapes", showShapes);
+					showConstraints = EditorGUILayout.Toggle("Show Constraints", showConstraints);
 				}
 				requireRepaint |= EditorGUI.EndChangeCheck();
 
@@ -168,6 +222,10 @@ namespace Spine.Unity.Editor {
 						using (new EditorGUILayout.FadeGroupScope(showSkeleton.faded)) {
 							EditorGUI.BeginChangeCheck();
 
+							EditorGUI.BeginDisabledGroup(true);
+							FalseDropDown(".Skin", skeleton.Skin != null ? skeletonRenderer.Skeleton.Skin.Name : "<None>", Icons.skin);
+							EditorGUI.EndDisabledGroup();
+
 							// Flip
 							EditorGUILayout.BeginHorizontal(GUILayout.MaxWidth(160f));
 							EditorGUILayout.LabelField("Flip", GUILayout.MaxWidth(EditorGUIUtility.labelWidth - 20f));
@@ -184,12 +242,13 @@ namespace Spine.Unity.Editor {
 				}
 
 				// Bone
-				showInspectBoneTree.target = EditorGUILayout.Foldout(showInspectBoneTree.target, new GUIContent("Bone", Icons.bone), BoldFoldoutStyle);
+				showInspectBoneTree.target = EditorGUILayout.Foldout(showInspectBoneTree.target, SpineInspectorUtility.TempContent("Bone", Icons.bone), BoldFoldoutStyle);
 				if (showInspectBoneTree.faded > 0) {
 					using (new SpineInspectorUtility.IndentScope()) {
 						using (new EditorGUILayout.FadeGroupScope(showInspectBoneTree.faded)) {
+							showBoneNames = EditorGUILayout.Toggle("Show Bone Names", showBoneNames);
 							if (bpo == null) bpo = new SerializedObject(this).FindProperty("boneName");
-							EditorGUILayout.PropertyField(bpo);
+							EditorGUILayout.PropertyField(bpo, SpineInspectorUtility.TempContent("Bone"));
 							if (!string.IsNullOrEmpty(bpo.stringValue)) {
 								if (bone == null || bone.Data.Name != bpo.stringValue) {
 									bone = skeleton.FindBone(bpo.stringValue);
@@ -197,18 +256,48 @@ namespace Spine.Unity.Editor {
 
 								if (bone != null) {
 									using (new EditorGUI.DisabledGroupScope(true)) {
+										var wm = EditorGUIUtility.wideMode;
+										EditorGUIUtility.wideMode = true;
+										EditorGUILayout.Slider("Local Rotation", ViewRound(bone.Rotation), -180f, 180f);
+										EditorGUILayout.Vector2Field("Local Position", RoundVector2(bone.X, bone.Y));
+										EditorGUILayout.Vector2Field("Local Scale", RoundVector2(bone.ScaleX, bone.ScaleY));
+										EditorGUILayout.Vector2Field("Local Shear", RoundVector2(bone.ShearX, bone.ShearY));
+
+										EditorGUILayout.Space();
+
 										var boneParent = bone.Parent;
-										if (boneParent != null) EditorGUILayout.TextField("parent", boneParent.Data.Name);
+										if (boneParent != null) FalseDropDown("Parent", boneParent.Data.Name, Icons.bone);
+
+										const string RoundFormat = "0.##";
+										var lw = EditorGUIUtility.labelWidth;
+										var fw = EditorGUIUtility.fieldWidth;
+										EditorGUIUtility.labelWidth *= 0.25f;
+										EditorGUIUtility.fieldWidth *= 0.5f;
+										EditorGUILayout.LabelField("LocalToWorld");
+
+										EditorGUILayout.BeginHorizontal();
 										EditorGUILayout.Space();
+										EditorGUILayout.TextField(".A", bone.A.ToString(RoundFormat));
+										EditorGUILayout.TextField(".B", bone.B.ToString(RoundFormat));
+										EditorGUILayout.EndHorizontal();
+										EditorGUILayout.BeginHorizontal();
+										EditorGUILayout.Space();
+										EditorGUILayout.TextField(".C", bone.C.ToString(RoundFormat));
+										EditorGUILayout.TextField(".D", bone.D.ToString(RoundFormat));
+										EditorGUILayout.EndHorizontal();
+
+										EditorGUIUtility.labelWidth = lw * 0.5f;
+										EditorGUILayout.BeginHorizontal();
+										EditorGUILayout.Space();
+										EditorGUILayout.Space();
+										EditorGUILayout.TextField(".WorldX", bone.WorldX.ToString(RoundFormat));
+										EditorGUILayout.TextField(".WorldY", bone.WorldY.ToString(RoundFormat));
+										EditorGUILayout.EndHorizontal();
+
+										EditorGUIUtility.labelWidth = lw;
+										EditorGUIUtility.fieldWidth = fw;
+										EditorGUIUtility.wideMode = wm;
 
-										EditorGUILayout.Slider("Local Rotation", bone.Rotation, -180f, 180f);
-										EditorGUILayout.Vector2Field("Local Position", new Vector2(bone.X, bone.Y));
-										EditorGUILayout.Vector2Field("Local Scale", new Vector2(bone.ScaleX, bone.ScaleY));
-										EditorGUILayout.Vector2Field("Local Shear", new Vector2(bone.ShearX, bone.ShearY));
-//										EditorGUILayout.Space();
-//										EditorGUILayout.LabelField("LocalToWorld Matrix");
-//										EditorGUILayout.Vector2Field("AB", new Vector2(bone.A, bone.B));
-//										EditorGUILayout.Vector2Field("CD", new Vector2(bone.C, bone.D));
 									}
 								}
 								requireRepaint = true;
@@ -224,7 +313,7 @@ namespace Spine.Unity.Editor {
 				showSlotsTree.target = EditorGUILayout.Foldout(showSlotsTree.target, SlotsRootLabel, BoldFoldoutStyle);
 				if (showSlotsTree.faded > 0) {
 					using (new EditorGUILayout.FadeGroupScope(showSlotsTree.faded)) {
-						if (SpineInspectorUtility.CenteredButton(new GUIContent("Skeleton.SetSlotsToSetupPose()"))) {
+						if (SpineInspectorUtility.CenteredButton(SpineInspectorUtility.TempContent("Skeleton.SetSlotsToSetupPose()"))) {
 							skeleton.SetSlotsToSetupPose();
 							requireRepaint = true;
 						}
@@ -235,7 +324,7 @@ namespace Spine.Unity.Editor {
 
 							using (new EditorGUILayout.HorizontalScope()) {
 								EditorGUI.indentLevel = baseIndent + 1;
-								EditorGUILayout.LabelField(new GUIContent(slot.Data.Name, Icons.slot), GUILayout.ExpandWidth(false));
+								EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(slot.Data.Name, Icons.slot), GUILayout.ExpandWidth(false));
 								EditorGUI.BeginChangeCheck();
 								Color c = EditorGUILayout.ColorField(new Color(slot.R, slot.G, slot.B, slot.A), GUILayout.Width(60));
 								if (EditorGUI.EndChangeCheck()) {
@@ -249,7 +338,7 @@ namespace Spine.Unity.Editor {
 								EditorGUI.indentLevel = baseIndent + 2;
 								var icon = Icons.GetAttachmentIcon(attachment);
 								bool isAttached = (attachment == slot.Attachment);
-								bool swap = EditorGUILayout.ToggleLeft(new GUIContent(attachment.Name, icon), attachment == slot.Attachment);
+								bool swap = EditorGUILayout.ToggleLeft(SpineInspectorUtility.TempContent(attachment.Name, icon), attachment == slot.Attachment);
 								if (isAttached != swap) {
 									slot.Attachment = isAttached ? null : attachment;
 									requireRepaint = true;
@@ -263,18 +352,24 @@ namespace Spine.Unity.Editor {
 
 				// Constraints
 				const string NoneText = "<none>";
-				showConstraintsTree.target = EditorGUILayout.Foldout(showConstraintsTree.target, new GUIContent("Constraints", Icons.constraintRoot), BoldFoldoutStyle);
+				showConstraintsTree.target = EditorGUILayout.Foldout(showConstraintsTree.target, SpineInspectorUtility.TempContent("Constraints", Icons.constraintRoot), BoldFoldoutStyle);
 				if (showConstraintsTree.faded > 0) {
 					using (new SpineInspectorUtility.IndentScope()) {
 						using (new EditorGUILayout.FadeGroupScope(showConstraintsTree.faded)) {
 							const float MixMin = 0f;
 							const float MixMax = 1f;
+							EditorGUI.BeginChangeCheck();
+							showConstraints = EditorGUILayout.Toggle("Show Constraints", showConstraints);
+							requireRepaint |= EditorGUI.EndChangeCheck();
+
+							EditorGUILayout.Space();
 
-							EditorGUILayout.LabelField(new GUIContent(string.Format("IK Constraints ({0})", skeleton.IkConstraints.Count), Icons.constraintIK), EditorStyles.boldLabel);
+							EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(string.Format("IK Constraints ({0})", skeleton.IkConstraints.Count), Icons.constraintIK), EditorStyles.boldLabel);
 							using (new SpineInspectorUtility.IndentScope()) {
 								if (skeleton.IkConstraints.Count > 0) {
 									foreach (var c in skeleton.IkConstraints) {
-										EditorGUILayout.LabelField(new GUIContent(c.Data.Name, Icons.constraintIK));
+										EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(c.Data.Name, Icons.constraintIK));
+										FalseDropDown("Goal", c.Data.Target.Name, Icons.bone, true);
 
 										EditorGUI.BeginChangeCheck();
 										c.Mix = EditorGUILayout.Slider("Mix", c.Mix, MixMin, MixMax);
@@ -289,11 +384,14 @@ namespace Spine.Unity.Editor {
 								}
 							}
 
-							EditorGUILayout.LabelField(new GUIContent(string.Format("Transform Constraints ({0})", skeleton.TransformConstraints.Count), Icons.constraintTransform), EditorStyles.boldLabel);
+							EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(string.Format("Transform Constraints ({0})", skeleton.TransformConstraints.Count), Icons.constraintTransform), EditorStyles.boldLabel);
 							using (new SpineInspectorUtility.IndentScope()) {
 								if (skeleton.TransformConstraints.Count > 0) {
 									foreach (var c in skeleton.TransformConstraints) {
-										EditorGUILayout.LabelField(new GUIContent(c.Data.Name, Icons.constraintTransform));
+										EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(c.Data.Name, Icons.constraintTransform));
+										EditorGUI.BeginDisabledGroup(true);
+										FalseDropDown("Goal", c.Data.Target.Name, Icons.bone);
+										EditorGUI.EndDisabledGroup();
 
 										EditorGUI.BeginChangeCheck();
 										c.TranslateMix = EditorGUILayout.Slider("TranslateMix", c.TranslateMix, MixMin, MixMax);
@@ -309,14 +407,24 @@ namespace Spine.Unity.Editor {
 								}
 							}
 
-							EditorGUILayout.LabelField(new GUIContent(string.Format("Path Constraints ({0})", skeleton.PathConstraints.Count), Icons.constraintPath), EditorStyles.boldLabel);
+							EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(string.Format("Path Constraints ({0})", skeleton.PathConstraints.Count), Icons.constraintPath), EditorStyles.boldLabel);
+
+							EditorGUI.BeginChangeCheck();
+							showPaths = EditorGUILayout.Toggle("Show Paths", showPaths);
+							requireRepaint |= EditorGUI.EndChangeCheck();
+
 							using (new SpineInspectorUtility.IndentScope()) {
 								if (skeleton.PathConstraints.Count > 0) {
 									foreach (var c in skeleton.PathConstraints) {
-										EditorGUILayout.LabelField(new GUIContent(c.Data.Name, Icons.constraintPath));
+										EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(c.Data.Name, Icons.constraintPath));
+										EditorGUI.BeginDisabledGroup(true);
+										FalseDropDown("Path Slot", c.Data.Target.Name, Icons.slot);
+										var activeAttachment = c.Target.Attachment;
+										FalseDropDown("Active Path", activeAttachment != null ? activeAttachment.Name : "<None>", activeAttachment is PathAttachment ? Icons.path : null);
 										EditorGUILayout.LabelField("PositionMode." + c.Data.PositionMode);
 										EditorGUILayout.LabelField("SpacingMode." + c.Data.SpacingMode);
 										EditorGUILayout.LabelField("RotateMode." + c.Data.RotateMode);
+										EditorGUI.EndDisabledGroup();
 
 										EditorGUI.BeginChangeCheck();
 										c.RotateMix = EditorGUILayout.Slider("RotateMix", c.RotateMix, MixMin, MixMax);
@@ -336,26 +444,28 @@ namespace Spine.Unity.Editor {
 					}
 				}
 
-				showDrawOrderTree.target = EditorGUILayout.Foldout(showDrawOrderTree.target, new GUIContent("Draw Order and Separators", Icons.slotRoot), BoldFoldoutStyle);
+				showDrawOrderTree.target = EditorGUILayout.Foldout(showDrawOrderTree.target, SpineInspectorUtility.TempContent("Draw Order and Separators", Icons.slotRoot), BoldFoldoutStyle);
 				if (showDrawOrderTree.faded > 0) {
 					using (new SpineInspectorUtility.IndentScope()) {
 						using (new EditorGUILayout.FadeGroupScope(showDrawOrderTree.faded)) {
 
+							const string SeparatorString = "------------- v SEPARATOR v -------------";
+
 							if (Application.isPlaying) {
 								foreach (var slot in skeleton.DrawOrder) {
-									if (skeletonRenderer.separatorSlots.Contains(slot))	EditorGUILayout.LabelField("------");
-									EditorGUILayout.LabelField(new GUIContent(slot.Data.Name, Icons.slot), GUILayout.ExpandWidth(false));
+									if (skeletonRenderer.separatorSlots.Contains(slot))	EditorGUILayout.LabelField(SeparatorString);
+									EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(slot.Data.Name, Icons.slot), GUILayout.ExpandWidth(false));
 								}
 							} else {
 								foreach (var slot in skeleton.DrawOrder) {
 									var slotNames = skeletonRenderer.separatorSlotNames;
 									for (int i = 0, n = slotNames.Length; i < n; i++) {
 										if (string.Equals(slotNames[i], slot.Data.Name, System.StringComparison.Ordinal)) {
-											EditorGUILayout.LabelField("------");
+											EditorGUILayout.LabelField(SeparatorString);
 											break;
 										}
 									}
-									EditorGUILayout.LabelField(new GUIContent(slot.Data.Name, Icons.slot), GUILayout.ExpandWidth(false));
+									EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(slot.Data.Name, Icons.slot), GUILayout.ExpandWidth(false));
 								}
 							}
 								
@@ -363,13 +473,13 @@ namespace Spine.Unity.Editor {
 					}
 				}
 
-				showEventDataTree.target = EditorGUILayout.Foldout(showEventDataTree.target, new GUIContent("Events", Icons.userEvent), BoldFoldoutStyle);
+				showEventDataTree.target = EditorGUILayout.Foldout(showEventDataTree.target, SpineInspectorUtility.TempContent("Events", Icons.userEvent), BoldFoldoutStyle);
 				if (showEventDataTree.faded > 0) {
 					using (new SpineInspectorUtility.IndentScope()) {
 						using (new EditorGUILayout.FadeGroupScope(showEventDataTree.faded)) {
 							if (skeleton.Data.Events.Count > 0) {
 								foreach (var e in skeleton.Data.Events) {
-									EditorGUILayout.LabelField(new GUIContent(e.Name, Icons.userEvent));
+									EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(e.Name, Icons.userEvent));
 								}
 							} else {
 								EditorGUILayout.LabelField(NoneText);
@@ -378,6 +488,8 @@ namespace Spine.Unity.Editor {
 					}
 				}
 
+				// TODO: Data counts. bones, slots, constraints, skins, etc...
+
 				if (IsAnimating(showSlotsTree, showSkeleton, showConstraintsTree, showDrawOrderTree, showEventDataTree, showInspectBoneTree))
 					Repaint();
 			}
@@ -391,6 +503,18 @@ namespace Spine.Unity.Editor {
 			EditorGUILayout.EndScrollView();
 		}
 
+		static float ViewRound (float x) {
+			const float Factor = 100f;
+			const float Divisor = 1f/Factor;
+			return Mathf.Round(x * Factor) * Divisor;
+		}
+
+		static Vector2 RoundVector2 (float x, float y) {
+			const float Factor = 100f;
+			const float Divisor = 1f/Factor;
+			return new Vector2(Mathf.Round(x * Factor) * Divisor, Mathf.Round(y * Factor) * Divisor);
+		}
+
 		static bool IsAnimating (params AnimBool[] animBools) {
 			foreach (var a in animBools)
 				if (a.isAnimating) return true;

+ 22 - 13
spine-unity/Assets/spine-unity/Editor/SkeletonRendererInspector.cs

@@ -42,7 +42,6 @@ namespace Spine.Unity.Editor {
 	[CanEditMultipleObjects]
 	public class SkeletonRendererInspector : UnityEditor.Editor {
 		protected static bool advancedFoldout;
-		internal static bool showBoneNames, showPaths, showShapes, showConstraints = true;
 
 		protected SerializedProperty skeletonDataAsset, initialSkinName;
 		protected SerializedProperty initialFlipX, initialFlipY;
@@ -131,6 +130,9 @@ namespace Spine.Unity.Editor {
 			}
 		}
 
+		GUIContent[] skins;
+		ExposedList<Skin> loadedSkinList;
+
 		protected virtual void DrawInspectorGUI (bool multi) {
 			bool valid = TargetIsValid;
 			var reloadWidth = GUILayout.Width(GUI.skin.label.CalcSize(new GUIContent(ReloadButtonLabel)).x + 20);
@@ -216,17 +218,27 @@ namespace Spine.Unity.Editor {
 
 				// Initial skin name.
 				if (component.valid) {
-					string[] skins = new string[component.skeleton.Data.Skins.Count];
+					var skeletonDataSkins = component.skeleton.Data.Skins;
+					int skinCount = skeletonDataSkins.Count;
+					if (loadedSkinList != skeletonDataSkins) {
+						skins = new GUIContent[skinCount];
+						loadedSkinList = skeletonDataSkins;
+						for (int i = 0; i < skins.Length; i++) {
+							string skinNameString = skeletonDataSkins.Items[i].Name;
+							skins[i] = new GUIContent(skinNameString, Icons.skin);
+						}
+					}
+					
 					int skinIndex = 0;
 					for (int i = 0; i < skins.Length; i++) {
-						string skinNameString = component.skeleton.Data.Skins.Items[i].Name;
-						skins[i] = skinNameString;
+						string skinNameString = skeletonDataSkins.Items[i].Name;
 						if (skinNameString == initialSkinName.stringValue)
 							skinIndex = i;
 					}
-					skinIndex = EditorGUILayout.Popup("Initial Skin", skinIndex, skins);
+
+					skinIndex = EditorGUILayout.Popup(SpineInspectorUtility.TempContent("Initial Skin"), skinIndex, skins);
 					if (skins.Length > 0) // Support attachmentless/skinless SkeletonData.
-						initialSkinName.stringValue = skins[skinIndex];
+						initialSkinName.stringValue = skins[skinIndex].text;
 				}
 			}
 
@@ -268,7 +280,6 @@ namespace Spine.Unity.Editor {
 							if (singleSubmesh != null) EditorGUILayout.PropertyField(singleSubmesh, SingleSubmeshLabel);
 							if (meshes != null) EditorGUILayout.PropertyField(meshes, MeshesLabel);
 							if (immutableTriangles != null) EditorGUILayout.PropertyField(immutableTriangles, ImmubleTrianglesLabel);
-							EditorGUILayout.PropertyField(tintBlack, TintBlackLabel);
 							EditorGUILayout.PropertyField(clearStateOnDisable, ClearStateOnDisableLabel);
 							EditorGUILayout.Space();
 						}
@@ -283,8 +294,10 @@ namespace Spine.Unity.Editor {
 						EditorGUILayout.Space();
 
 						using (new SpineInspectorUtility.LabelWidthScope()) {
-							EditorGUILayout.LabelField("Vertex Data", EditorStyles.boldLabel);
+							//EditorGUILayout.LabelField("Vertex Data", EditorStyles.boldLabel);
+							EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Vertex Data", EditorGUIUtility.ObjectContent(null, typeof(MeshFilter)).image as Texture2D), EditorStyles.boldLabel);
 							if (pmaVertexColors != null) EditorGUILayout.PropertyField(pmaVertexColors, PMAVertexColorsLabel);
+							EditorGUILayout.PropertyField(tintBlack, TintBlackLabel);
 
 							// Optional fields. May be disabled in SkeletonRenderer.
 							if (normals != null) EditorGUILayout.PropertyField(normals, NormalsLabel);
@@ -344,7 +357,7 @@ namespace Spine.Unity.Editor {
 			using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) {
 				const string SeparatorsDescription = "Stored names of slots where the Skeleton's render will be split into different batches. This is used by separate components that split the render into different MeshRenderers or GameObjects.";
 				if (separatorSlotNames.isExpanded) {
-					EditorGUILayout.PropertyField(separatorSlotNames, new GUIContent(separatorSlotNames.displayName + terminalSlotWarning, SeparatorsDescription), true);
+					EditorGUILayout.PropertyField(separatorSlotNames, SpineInspectorUtility.TempContent(separatorSlotNames.displayName + terminalSlotWarning, Icons.slotRoot, SeparatorsDescription), true);
 					GUILayout.BeginHorizontal();
 					GUILayout.FlexibleSpace();
 					if (GUILayout.Button("+", GUILayout.MaxWidth(28f), GUILayout.MaxHeight(15f))) {
@@ -364,11 +377,7 @@ namespace Spine.Unity.Editor {
 			var transform = skeletonRenderer.transform;
 			if (skeleton == null) return;
 
-			if (showPaths) SpineHandles.DrawPaths(transform, skeleton);
 			SpineHandles.DrawBones(transform, skeleton);
-			if (showConstraints) SpineHandles.DrawConstraints(transform, skeleton);
-			if (showBoneNames) SpineHandles.DrawBoneNames(transform, skeleton);
-			if (showShapes) SpineHandles.DrawBoundingBoxes(transform, skeleton);
 		}
 
 		override public void OnInspectorGUI () {

+ 38 - 10
spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs

@@ -50,11 +50,22 @@ namespace Spine.Unity.Editor {
 
 	public abstract class SpineTreeItemDrawerBase<T> : PropertyDrawer where T:SpineAttributeBase {
 		protected SkeletonDataAsset skeletonDataAsset;
-		internal const string NoneLabel = "<None>";
-
+		internal const string NoneString = "<None>";
+
+		// Analysis disable once StaticFieldInGenericType
+		static GUIContent noneLabel;
+		static GUIContent NoneLabel (Texture2D image = null) {
+			if (noneLabel == null)
+				noneLabel = new GUIContent(NoneString);
+			noneLabel.image = image;
+			return noneLabel;
+		}
+			
 		protected T TargetAttribute { get { return (T)attribute; } }
 		protected SerializedProperty SerializedProperty { get; private set; }
 
+		protected abstract Texture2D Icon { get; }
+
 		public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
 			SerializedProperty = property;
 
@@ -89,11 +100,10 @@ namespace Spine.Unity.Editor {
 			}
 
 			position = EditorGUI.PrefixLabel(position, label);
-
+			var image = Icon;
 			var propertyStringValue = (property.hasMultipleDifferentValues) ? SpineInspectorUtility.EmDash : property.stringValue;
-			if (GUI.Button(position, string.IsNullOrEmpty(propertyStringValue) ? NoneLabel : propertyStringValue, EditorStyles.popup))
+			if (GUI.Button(position, string.IsNullOrEmpty(propertyStringValue) ? NoneLabel(image) : SpineInspectorUtility.TempContent(propertyStringValue, image), EditorStyles.popup))
 				Selector(property);
-
 		}
 
 		public ISkeletonComponent GetTargetSkeletonComponent (SerializedProperty property) {
@@ -138,6 +148,8 @@ namespace Spine.Unity.Editor {
 	[CustomPropertyDrawer(typeof(SpineSlot))]
 	public class SpineSlotDrawer : SpineTreeItemDrawerBase<SpineSlot> {
 
+		protected override Texture2D Icon {	get { return SpineEditorUtilities.Icons.slot; } }
+
 		protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineSlot targetAttribute, SkeletonData data) {
 			for (int i = 0; i < data.Slots.Count; i++) {
 				string name = data.Slots.Items[i].Name;
@@ -177,6 +189,8 @@ namespace Spine.Unity.Editor {
 	[CustomPropertyDrawer(typeof(SpineSkin))]
 	public class SpineSkinDrawer : SpineTreeItemDrawerBase<SpineSkin> {
 
+		protected override Texture2D Icon {	get { return SpineEditorUtilities.Icons.skin; } }
+
 		protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineSkin targetAttribute, SkeletonData data) {
 			menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name));
 			menu.AddSeparator("");
@@ -192,11 +206,14 @@ namespace Spine.Unity.Editor {
 
 	[CustomPropertyDrawer(typeof(SpineAnimation))]
 	public class SpineAnimationDrawer : SpineTreeItemDrawerBase<SpineAnimation> {
+		
+		protected override Texture2D Icon {	get { return SpineEditorUtilities.Icons.animation; } }
+
 		protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineAnimation targetAttribute, SkeletonData data) {
 			var animations = skeletonDataAsset.GetAnimationStateData().SkeletonData.Animations;
 
 			// <None> item
-			menu.AddItem(new GUIContent(NoneLabel), string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair("", property));
+			menu.AddItem(new GUIContent(NoneString), string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair(string.Empty, property));
 
 			for (int i = 0; i < animations.Count; i++) {
 				string name = animations.Items[i].Name;
@@ -209,11 +226,14 @@ namespace Spine.Unity.Editor {
 
 	[CustomPropertyDrawer(typeof(SpineEvent))]
 	public class SpineEventNameDrawer : SpineTreeItemDrawerBase<SpineEvent> {
+
+		protected override Texture2D Icon {	get { return SpineEditorUtilities.Icons.userEvent; } }
+
 		protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineEvent targetAttribute, SkeletonData data) {
 			var events = skeletonDataAsset.GetSkeletonData(false).Events;
 
 			// <None> item
-			menu.AddItem(new GUIContent(NoneLabel), string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair("", property));
+			menu.AddItem(new GUIContent(NoneString), string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair(string.Empty, property));
 
 			for (int i = 0; i < events.Count; i++) {
 				string name = events.Items[i].Name;
@@ -226,6 +246,9 @@ namespace Spine.Unity.Editor {
 
 	[CustomPropertyDrawer(typeof(SpineAttachment))]
 	public class SpineAttachmentDrawer : SpineTreeItemDrawerBase<SpineAttachment> {
+
+		protected override Texture2D Icon {	get { return SpineEditorUtilities.Icons.genericAttachment; } }
+
 		protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineAttachment targetAttribute, SkeletonData data) {
 			ISkeletonComponent skeletonComponent = GetTargetSkeletonComponent(property);
 			var validSkins = new List<Skin>();
@@ -252,7 +275,8 @@ namespace Spine.Unity.Editor {
 				menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name));
 
 			menu.AddSeparator("");
-			menu.AddItem(new GUIContent("Null"), property.stringValue == "", HandleSelect, new SpineDrawerValuePair("", property));
+			const string NullAttachmentName = "";
+			menu.AddItem(new GUIContent("Null"), property.stringValue == NullAttachmentName, HandleSelect, new SpineDrawerValuePair(NullAttachmentName, property));
 			menu.AddSeparator("");
 
 			Skin defaultSkin = data.Skins.Items[0];
@@ -260,9 +284,8 @@ namespace Spine.Unity.Editor {
 			SerializedProperty slotProperty = property.serializedObject.FindProperty(targetAttribute.slotField);
 			string slotMatch = "";
 			if (slotProperty != null) {
-				if (slotProperty.propertyType == SerializedPropertyType.String) {
+				if (slotProperty.propertyType == SerializedPropertyType.String)
 					slotMatch = slotProperty.stringValue.ToLower();
-				}
 			}
 
 			foreach (Skin skin in validSkins) {
@@ -307,10 +330,15 @@ namespace Spine.Unity.Editor {
 
 	[CustomPropertyDrawer(typeof(SpineBone))]
 	public class SpineBoneDrawer : SpineTreeItemDrawerBase<SpineBone> {
+		
+		protected override Texture2D Icon {	get { return SpineEditorUtilities.Icons.bone; } }
+
 		protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineBone targetAttribute, SkeletonData data) {
 			menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name));
 			menu.AddSeparator("");
 
+			menu.AddItem(new GUIContent(NoneString), string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair(string.Empty, property));
+
 			for (int i = 0; i < data.Bones.Count; i++) {
 				string name = data.Bones.Items[i].Name;
 				if (name.StartsWith(targetAttribute.startsWith, StringComparison.Ordinal))

+ 33 - 3
spine-unity/Assets/spine-unity/Editor/SpineEditorUtilities.cs

@@ -59,6 +59,7 @@ namespace Spine.Unity.Editor {
 			public static Texture2D slotRoot;
 			public static Texture2D skinPlaceholder;
 			public static Texture2D image;
+			public static Texture2D genericAttachment;
 			public static Texture2D boundingBox;
 			public static Texture2D mesh;
 			public static Texture2D weights;
@@ -90,11 +91,13 @@ namespace Spine.Unity.Editor {
 				slot = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-slot.png");
 				slotRoot = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-slotRoot.png");
 				skinPlaceholder = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skinPlaceholder.png");
+
+				genericAttachment = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-attachment.png");
 				image = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-image.png");
 				boundingBox = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-boundingBox.png");
 				mesh = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-mesh.png");
 				weights = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-weights.png");
-				skin = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skinPlaceholder.png");
+				skin = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skin.png");
 				skinsRoot = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-skinsRoot.png");
 				animation = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-animation.png");
 				animationRoot = (Texture2D)AssetDatabase.LoadMainAssetAtPath(SpineEditorUtilities.editorGUIPath + "/icon-animationRoot.png");
@@ -1559,15 +1562,25 @@ namespace Spine.Unity.Editor {
 		}
 
 		static Material _boneMaterial;
-		public static Material BoneMaterial {
+		static Material BoneMaterial {
 			get {
 				if (_boneMaterial == null) {
 					_boneMaterial = new Material(Shader.Find("Hidden/Spine/Bones"));
 					_boneMaterial.SetColor("_Color", SpineHandles.BoneColor);
 				}
+
 				return _boneMaterial;
 			}
 		}
+		public static Material GetBoneMaterial () {
+			BoneMaterial.SetColor("_Color", SpineHandles.BoneColor);
+			return BoneMaterial;
+		}
+
+		public static Material GetBoneMaterial (Color color) {
+			BoneMaterial.SetColor("_Color", color);
+			return BoneMaterial;
+		}
 
 		static Material _ikMaterial;
 		public static Material IKMaterial {
@@ -1661,7 +1674,7 @@ namespace Spine.Unity.Editor {
 				const float my = 1.5f;
 				scale.y *= (SpineHandles.handleScale + 1f) * 0.5f;
 				scale.y = Mathf.Clamp(scale.x, -my, my);
-				SpineHandles.BoneMaterial.SetPass(0);
+				SpineHandles.GetBoneMaterial().SetPass(0);
 				Graphics.DrawMeshNow(SpineHandles.BoneMesh, transform.localToWorldMatrix * Matrix4x4.TRS(pos, rot, scale));
 			} else {
 				var wp = transform.TransformPoint(pos);
@@ -1669,6 +1682,23 @@ namespace Spine.Unity.Editor {
 			}
 		}
 
+		public static void DrawBone (Transform transform, Bone b, float boneScale, Color color) {
+			var pos = new Vector3(b.WorldX, b.WorldY, 0);
+			float length = b.Data.Length;
+			if (length > 0) {
+				Quaternion rot = Quaternion.Euler(0, 0, b.WorldRotationX);
+				Vector3 scale = Vector3.one * length * b.WorldScaleX;
+				const float my = 1.5f;
+				scale.y *= (SpineHandles.handleScale + 1f) * 0.5f;
+				scale.y = Mathf.Clamp(scale.x, -my, my);
+				SpineHandles.GetBoneMaterial(color).SetPass(0);
+				Graphics.DrawMeshNow(SpineHandles.BoneMesh, transform.localToWorldMatrix * Matrix4x4.TRS(pos, rot, scale));
+			} else {
+				var wp = transform.TransformPoint(pos);
+				DrawBoneCircle(wp, color, transform.forward, boneScale);
+			}
+		}
+
 		public static void DrawPaths (Transform transform, Skeleton skeleton) {
 			foreach (Slot s in skeleton.DrawOrder) {
 				var p = s.Attachment as PathAttachment;

+ 16 - 4
spine-unity/Assets/spine-unity/Editor/SpineInspectorUtility.cs

@@ -48,25 +48,37 @@ namespace Spine.Unity.Editor {
 			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 ?? new GUIContent(property.displayName, property.tooltip));
+			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 ?? new GUIContent(property.displayName, property.tooltip);
-			float width = GUI.skin.label.CalcSize(new GUIContent(label.text)).x + extraSpace;
+			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);
-
 		}
 
 		public static bool UndoRedoPerformed (UnityEngine.Event current) {
 			return current.type == EventType.ValidateCommand && current.commandName == "UndoRedoPerformed";
 		}
 
+		public static Texture2D UnityIcon (System.Type type) {
+			return EditorGUIUtility.ObjectContent(null, type).image as Texture2D;
+		}
+
 		#region Layout Scopes
 		static GUIStyle grayMiniLabel;
 		public static GUIStyle GrayMiniLabel {

+ 1 - 1
spine-unity/Assets/spine-unity/Modules/CustomMaterials/Editor/SkeletonRendererCustomMaterialsInspector.cs

@@ -136,7 +136,7 @@ namespace Spine.Unity.Editor {
 			_customMaterialOverridesPrev = CopyList(componentCustomMaterialOverrides);
 			_customSlotMaterialsPrev = CopyList(componentCustomSlotMaterials);
 
-			if (SpineInspectorUtility.LargeCenteredButton(new GUIContent("Clear and Reapply Changes", "Removes all non-serialized overrides in the SkeletonRenderer and reapplies the overrides on this component."))) {
+			if (SpineInspectorUtility.LargeCenteredButton(SpineInspectorUtility.TempContent("Clear and Reapply Changes", tooltip: "Removes all non-serialized overrides in the SkeletonRenderer and reapplies the overrides on this component."))) {
 				if (skeletonRenderer != null) {
 					#if SPINE_OPTIONAL_MATERIALOVERRIDE
 					skeletonRenderer.CustomMaterialOverride.Clear();

+ 2 - 2
spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs

@@ -84,7 +84,7 @@ namespace Spine.Unity.Editor {
 				return;
 			}
 			using (new SpineInspectorUtility.BoxScope()) {
-				EditorGUILayout.PropertyField(meshGeneratorSettings_, new GUIContent("Advanced..."), includeChildren: true);
+				EditorGUILayout.PropertyField(meshGeneratorSettings_, SpineInspectorUtility.TempContent("Advanced..."), includeChildren: true);
 			}
 
 			EditorGUILayout.Space();
@@ -94,7 +94,7 @@ namespace Spine.Unity.Editor {
 			EditorGUILayout.PropertyField(startingAnimation_);
 			EditorGUILayout.PropertyField(startingLoop_);
 			EditorGUILayout.PropertyField(timeScale_);
-			EditorGUILayout.PropertyField(unscaledTime_, new GUIContent(unscaledTime_.displayName, "If checked, this will use Time.unscaledDeltaTime to make this update independent of game Time.timeScale. Instance SkeletonGraphic.timeScale will still be applied."));
+			EditorGUILayout.PropertyField(unscaledTime_, SpineInspectorUtility.TempContent(unscaledTime_.displayName, tooltip: "If checked, this will use Time.unscaledDeltaTime to make this update independent of game Time.timeScale. Instance SkeletonGraphic.timeScale will still be applied."));
 			EditorGUILayout.Space();
 			EditorGUILayout.PropertyField(freeze_);
 			EditorGUILayout.Space();

+ 7 - 0
spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonPartsRendererInspector.cs

@@ -43,6 +43,13 @@ namespace Spine.Unity.Modules {
 
 		public override void OnInspectorGUI () {
 			SpineInspectorUtility.SortingPropertyFields(sortingProperties, true);
+			EditorGUILayout.Space();
+			if (SpineInspectorUtility.LargeCenteredButton(new GUIContent("Select SkeletonRenderer", SpineEditorUtilities.Icons.spine))) {
+				var thisSkeletonPartsRenderer = target as SkeletonPartsRenderer;
+				var srs = thisSkeletonPartsRenderer.GetComponentInParent<SkeletonRenderSeparator>();
+				if (srs != null && srs.partsRenderers.Contains(thisSkeletonPartsRenderer) && srs.SkeletonRenderer != null)
+					Selection.activeGameObject = srs.SkeletonRenderer.gameObject;
+			}
 		}
 	}
 

+ 2 - 0
spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonRenderSeparatorInspector.cs

@@ -85,6 +85,8 @@ namespace Spine.Unity.Modules {
 				bool checkBox = EditorGUILayout.Toggle("Enable Separator", componentEnabled);
 				if (checkBox != componentEnabled)
 					component.enabled = checkBox;
+				if (component.SkeletonRenderer.disableRenderingOnOverride && !component.enabled)
+					EditorGUILayout.HelpBox("By default, SkeletonRenderer's MeshRenderer is disabled while the SkeletonRenderSeparator takes over rendering. It is re-enabled when SkeletonRenderSeparator is disabled.", MessageType.Info);
 
 				EditorGUILayout.PropertyField(copyPropertyBlock_);
 				EditorGUILayout.PropertyField(copyMeshRendererFlags_);

+ 16 - 2
spine-unity/Assets/spine-unity/Modules/SkeletonRenderSeparator/SkeletonRenderSeparator.cs

@@ -58,9 +58,9 @@ namespace Spine.Unity.Modules {
 		}
 
 		MeshRenderer mainMeshRenderer;
-		public bool copyPropertyBlock = false;
+		public bool copyPropertyBlock = true;
 		[Tooltip("Copies MeshRenderer flags into each parts renderer")]
-		public bool copyMeshRendererFlags = false;
+		public bool copyMeshRendererFlags = true;
 		public List<Spine.Unity.Modules.SkeletonPartsRenderer> partsRenderers = new List<SkeletonPartsRenderer>();
 
 		#if UNITY_EDITOR
@@ -117,6 +117,13 @@ namespace Spine.Unity.Modules {
 				var lightProbeUsage = mainMeshRenderer.lightProbeUsage;
 				bool receiveShadows = mainMeshRenderer.receiveShadows;
 
+				#if UNITY_5_5_OR_NEWER
+				var reflectionProbeUsage = mainMeshRenderer.reflectionProbeUsage;
+				var shadowCastingMode = mainMeshRenderer.shadowCastingMode;
+				var motionVectorGenerationMode = mainMeshRenderer.motionVectorGenerationMode;
+				var probeAnchor = mainMeshRenderer.probeAnchor;
+				#endif
+
 				for (int i = 0; i < partsRenderers.Count; i++) {
 					var currentRenderer = partsRenderers[i];
 					if (currentRenderer == null) continue; // skip null items.
@@ -124,6 +131,13 @@ namespace Spine.Unity.Modules {
 					var mr = currentRenderer.MeshRenderer;
 					mr.lightProbeUsage = lightProbeUsage;
 					mr.receiveShadows = receiveShadows;
+
+					#if UNITY_5_5_OR_NEWER
+					mr.reflectionProbeUsage = reflectionProbeUsage;
+					mr.shadowCastingMode = shadowCastingMode;
+					mr.motionVectorGenerationMode = motionVectorGenerationMode;
+					mr.probeAnchor = probeAnchor;
+					#endif
 				}
 			}
 			#else

+ 4 - 4
spine-unity/Assets/spine-unity/SkeletonUtility/Editor/SkeletonUtilityBoneInspector.cs

@@ -164,11 +164,11 @@ namespace Spine.Unity.Editor {
 			using (new GUILayout.HorizontalScope()) {
 				EditorGUILayout.Space();
 				using (new EditorGUI.DisabledGroupScope(multiObject || !utilityBone.valid || utilityBone.bone == null || utilityBone.bone.Children.Count == 0)) {
-					if (GUILayout.Button(new GUIContent("Add Child", Icons.bone), GUILayout.MinWidth(120), GUILayout.Height(24)))
+					if (GUILayout.Button(SpineInspectorUtility.TempContent("Add Child", Icons.bone), GUILayout.MinWidth(120), GUILayout.Height(24)))
 						BoneSelectorContextMenu("", utilityBone.bone.Children, "<Recursively>", SpawnChildBoneSelected);
 				}
 				using (new EditorGUI.DisabledGroupScope(multiObject || !utilityBone.valid || utilityBone.bone == null || containsOverrides)) {
-					if (GUILayout.Button(new GUIContent("Add Override", Icons.poseBones), GUILayout.MinWidth(120), GUILayout.Height(24)))
+					if (GUILayout.Button(SpineInspectorUtility.TempContent("Add Override", Icons.poseBones), GUILayout.MinWidth(120), GUILayout.Height(24)))
 						SpawnOverride();
 				}
 				EditorGUILayout.Space();
@@ -177,14 +177,14 @@ namespace Spine.Unity.Editor {
 			using (new GUILayout.HorizontalScope()) {
 				EditorGUILayout.Space();
 				using (new EditorGUI.DisabledGroupScope(multiObject || !utilityBone.valid || !canCreateHingeChain)) {
-					if (GUILayout.Button(new GUIContent("Create Hinge Chain", Icons.hingeChain), GUILayout.Width(150), GUILayout.Height(24)))
+					if (GUILayout.Button(SpineInspectorUtility.TempContent("Create Hinge Chain", Icons.hingeChain), GUILayout.Width(150), GUILayout.Height(24)))
 						CreateHingeChain();
 				}
 				EditorGUILayout.Space();
 			}
 
 			using (new EditorGUI.DisabledGroupScope(multiObject || boundingBoxTable.Count == 0)) {
-				EditorGUILayout.LabelField(new GUIContent("Bounding Boxes", Icons.boundingBox), EditorStyles.boldLabel);
+				EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Bounding Boxes", Icons.boundingBox), EditorStyles.boldLabel);
 
 				foreach (var entry in boundingBoxTable){
 					Slot slot = entry.Key;

+ 3 - 0
spine-unity/Assets/spine-unity/SpineAttributes.cs

@@ -31,9 +31,12 @@
 // Contributed by: Mitch Thompson
 
 using UnityEngine;
+using System;
 using System.Collections;
 
 namespace Spine.Unity {
+
+	[AttributeUsage(AttributeTargets.Field, Inherited = true, AllowMultiple = false)]
 	public abstract class SpineAttributeBase : PropertyAttribute {
 		public string dataField = "";
 		public string startsWith = "";