You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

501 lines
21 KiB

  1. using UnityEngine;
  2. using UnityEditor;
  3. using UnityEditorInternal;
  4. using System.Collections;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. namespace TMPro.EditorUtilities
  8. {
  9. [CustomEditor(typeof(TMP_SpriteAsset))]
  10. public class TMP_SpriteAssetEditor : Editor
  11. {
  12. struct UI_PanelState
  13. {
  14. public static bool spriteAssetInfoPanel = true;
  15. public static bool fallbackSpriteAssetPanel = true;
  16. public static bool spriteInfoPanel;
  17. }
  18. int m_moveToIndex;
  19. int m_selectedElement = -1;
  20. int m_page;
  21. const string k_UndoRedo = "UndoRedoPerformed";
  22. string m_searchPattern;
  23. List<int> m_searchList;
  24. bool m_isSearchDirty;
  25. SerializedProperty m_spriteAtlas_prop;
  26. SerializedProperty m_material_prop;
  27. SerializedProperty m_spriteInfoList_prop;
  28. ReorderableList m_fallbackSpriteAssetList;
  29. bool isAssetDirty;
  30. float m_xOffset;
  31. float m_yOffset;
  32. float m_xAdvance;
  33. float m_scale;
  34. public void OnEnable()
  35. {
  36. m_spriteAtlas_prop = serializedObject.FindProperty("spriteSheet");
  37. m_material_prop = serializedObject.FindProperty("material");
  38. m_spriteInfoList_prop = serializedObject.FindProperty("spriteInfoList");
  39. // Fallback TMP Sprite Asset list
  40. m_fallbackSpriteAssetList = new ReorderableList(serializedObject, serializedObject.FindProperty("fallbackSpriteAssets"), true, true, true, true);
  41. m_fallbackSpriteAssetList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
  42. {
  43. var element = m_fallbackSpriteAssetList.serializedProperty.GetArrayElementAtIndex(index);
  44. rect.y += 2;
  45. EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), element, GUIContent.none);
  46. };
  47. m_fallbackSpriteAssetList.drawHeaderCallback = rect =>
  48. {
  49. EditorGUI.LabelField(rect, new GUIContent("Fallback Sprite Asset List", "Select the Sprite Assets that will be searched and used as fallback when a given sprite is missing from this sprite asset."));
  50. };
  51. }
  52. public override void OnInspectorGUI()
  53. {
  54. //Debug.Log("OnInspectorGUI Called.");
  55. Event currentEvent = Event.current;
  56. string evt_cmd = currentEvent.commandName; // Get Current Event CommandName to check for Undo Events
  57. serializedObject.Update();
  58. // TEXTMESHPRO SPRITE INFO PANEL
  59. GUILayout.Label("Sprite Info", EditorStyles.boldLabel);
  60. EditorGUI.indentLevel = 1;
  61. EditorGUI.BeginChangeCheck();
  62. EditorGUILayout.PropertyField(m_spriteAtlas_prop , new GUIContent("Sprite Atlas"));
  63. if (EditorGUI.EndChangeCheck())
  64. {
  65. // Assign the new sprite atlas texture to the current material
  66. Texture2D tex = m_spriteAtlas_prop.objectReferenceValue as Texture2D;
  67. if (tex != null)
  68. {
  69. Material mat = m_material_prop.objectReferenceValue as Material;
  70. if (mat != null)
  71. mat.mainTexture = tex;
  72. }
  73. }
  74. EditorGUILayout.PropertyField(m_material_prop, new GUIContent("Default Material"));
  75. EditorGUILayout.Space();
  76. // FALLBACK SPRITE ASSETS
  77. EditorGUI.indentLevel = 0;
  78. UI_PanelState.fallbackSpriteAssetPanel = EditorGUILayout.Foldout(UI_PanelState.fallbackSpriteAssetPanel, new GUIContent("Fallback Sprite Assets", "Select the Sprite Assets that will be searched and used as fallback when a given sprite is missing from this sprite asset."), true, TMP_UIStyleManager.boldFoldout);
  79. if (UI_PanelState.fallbackSpriteAssetPanel)
  80. {
  81. m_fallbackSpriteAssetList.DoLayoutList();
  82. }
  83. // SPRITE LIST
  84. EditorGUI.indentLevel = 0;
  85. UI_PanelState.spriteInfoPanel = EditorGUILayout.Foldout(UI_PanelState.spriteInfoPanel, "Sprite List", true, TMP_UIStyleManager.boldFoldout);
  86. if (UI_PanelState.spriteInfoPanel)
  87. {
  88. int arraySize = m_spriteInfoList_prop.arraySize;
  89. int itemsPerPage = 10; // (Screen.height - 292) / 80;
  90. // Display Glyph Management Tools
  91. EditorGUILayout.BeginVertical(EditorStyles.helpBox, GUILayout.ExpandWidth(true));
  92. {
  93. // Search Bar implementation
  94. #region DISPLAY SEARCH BAR
  95. EditorGUILayout.BeginHorizontal();
  96. {
  97. EditorGUIUtility.labelWidth = 110f;
  98. EditorGUI.BeginChangeCheck();
  99. string searchPattern = EditorGUILayout.TextField("Sprite Search", m_searchPattern, "SearchTextField");
  100. if (EditorGUI.EndChangeCheck() || m_isSearchDirty)
  101. {
  102. if (string.IsNullOrEmpty(searchPattern) == false)
  103. {
  104. //GUIUtility.keyboardControl = 0;
  105. m_searchPattern = searchPattern.ToLower(System.Globalization.CultureInfo.InvariantCulture).Trim();
  106. // Search Glyph Table for potential matches
  107. SearchGlyphTable(m_searchPattern, ref m_searchList);
  108. }
  109. m_isSearchDirty = false;
  110. }
  111. string styleName = string.IsNullOrEmpty(m_searchPattern) ? "SearchCancelButtonEmpty" : "SearchCancelButton";
  112. if (GUILayout.Button(GUIContent.none, styleName))
  113. {
  114. GUIUtility.keyboardControl = 0;
  115. m_searchPattern = string.Empty;
  116. }
  117. }
  118. EditorGUILayout.EndHorizontal();
  119. #endregion
  120. // Display Page Navigation
  121. if (!string.IsNullOrEmpty(m_searchPattern))
  122. arraySize = m_searchList.Count;
  123. // Display Page Navigation
  124. DisplayGlyphPageNavigation(arraySize, itemsPerPage);
  125. }
  126. EditorGUILayout.EndVertical();
  127. if (arraySize > 0)
  128. {
  129. // Display each SpriteInfo entry using the SpriteInfo property drawer.
  130. for (int i = itemsPerPage * m_page; i < arraySize && i < itemsPerPage * (m_page + 1); i++)
  131. {
  132. // Define the start of the selection region of the element.
  133. Rect elementStartRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true));
  134. int elementIndex = i;
  135. if (!string.IsNullOrEmpty(m_searchPattern))
  136. elementIndex = m_searchList[i];
  137. SerializedProperty spriteInfo = m_spriteInfoList_prop.GetArrayElementAtIndex(elementIndex);
  138. EditorGUILayout.BeginVertical(EditorStyles.helpBox, GUILayout.Height(75));
  139. {
  140. EditorGUI.BeginDisabledGroup(i != m_selectedElement);
  141. {
  142. EditorGUILayout.PropertyField(spriteInfo);
  143. }
  144. EditorGUI.EndDisabledGroup();
  145. }
  146. EditorGUILayout.EndVertical();
  147. // Define the end of the selection region of the element.
  148. Rect elementEndRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true));
  149. // Check for Item selection
  150. Rect selectionArea = new Rect(elementStartRegion.x, elementStartRegion.y, elementEndRegion.width, elementEndRegion.y - elementStartRegion.y);
  151. if (DoSelectionCheck(selectionArea))
  152. {
  153. if (m_selectedElement == i)
  154. {
  155. m_selectedElement = -1;
  156. }
  157. else
  158. {
  159. m_selectedElement = i;
  160. GUIUtility.keyboardControl = 0;
  161. }
  162. }
  163. // Draw & Handle Section Area
  164. if (m_selectedElement == i)
  165. {
  166. // Draw selection highlight
  167. TMP_EditorUtility.DrawBox(selectionArea, 2f, new Color32(40, 192, 255, 255));
  168. // Draw options to MoveUp, MoveDown, Add or Remove Sprites
  169. Rect controlRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight * 1f);
  170. controlRect.width /= 8;
  171. // Move sprite up.
  172. bool guiEnabled = GUI.enabled;
  173. if (i == 0) { GUI.enabled = false; }
  174. if (GUI.Button(controlRect, "Up"))
  175. {
  176. SwapSpriteElement(i, i - 1);
  177. }
  178. GUI.enabled = guiEnabled;
  179. // Move sprite down.
  180. controlRect.x += controlRect.width;
  181. if (i == arraySize - 1) { GUI.enabled = false; }
  182. if (GUI.Button(controlRect, "Down"))
  183. {
  184. SwapSpriteElement(i, i + 1);
  185. }
  186. GUI.enabled = guiEnabled;
  187. // Move sprite to new index
  188. controlRect.x += controlRect.width * 2;
  189. //if (i == arraySize - 1) { GUI.enabled = false; }
  190. m_moveToIndex = EditorGUI.IntField(controlRect, m_moveToIndex);
  191. controlRect.x -= controlRect.width;
  192. if (GUI.Button(controlRect, "Goto"))
  193. {
  194. MoveSpriteElement(i, m_moveToIndex);
  195. }
  196. //controlRect.x += controlRect.width;
  197. GUI.enabled = guiEnabled;
  198. // Add new Sprite
  199. controlRect.x += controlRect.width * 4;
  200. if (GUI.Button(controlRect, "+"))
  201. {
  202. m_spriteInfoList_prop.arraySize += 1;
  203. int index = m_spriteInfoList_prop.arraySize - 1;
  204. SerializedProperty spriteInfo_prop = m_spriteInfoList_prop.GetArrayElementAtIndex(index);
  205. // Copy properties of the selected element
  206. CopySerializedProperty(m_spriteInfoList_prop.GetArrayElementAtIndex(elementIndex), ref spriteInfo_prop);
  207. spriteInfo_prop.FindPropertyRelative("id").intValue = index;
  208. serializedObject.ApplyModifiedProperties();
  209. m_isSearchDirty = true;
  210. }
  211. // Delete selected Sprite
  212. controlRect.x += controlRect.width;
  213. if (m_selectedElement == -1) GUI.enabled = false;
  214. if (GUI.Button(controlRect, "-"))
  215. {
  216. m_spriteInfoList_prop.DeleteArrayElementAtIndex(elementIndex);
  217. m_selectedElement = -1;
  218. serializedObject.ApplyModifiedProperties();
  219. m_isSearchDirty = true;
  220. return;
  221. }
  222. }
  223. }
  224. }
  225. DisplayGlyphPageNavigation(arraySize, itemsPerPage);
  226. EditorGUIUtility.labelWidth = 40f;
  227. EditorGUIUtility.fieldWidth = 20f;
  228. GUILayout.Space(5f);
  229. // GLOBAL TOOLS
  230. #region Global Tools
  231. GUI.enabled = true;
  232. EditorGUILayout.BeginVertical(EditorStyles.helpBox);
  233. Rect rect = EditorGUILayout.GetControlRect(false, 40);
  234. float width = (rect.width - 75f) / 4;
  235. EditorGUI.LabelField(rect, "Global Offsets & Scale", EditorStyles.boldLabel);
  236. rect.x += 70;
  237. bool old_ChangedState = GUI.changed;
  238. GUI.changed = false;
  239. m_xOffset = EditorGUI.FloatField(new Rect(rect.x + 5f + width * 0, rect.y + 20, width - 5f, 18), new GUIContent("OX:"), m_xOffset);
  240. if (GUI.changed) UpdateGlobalProperty("xOffset", m_xOffset);
  241. m_yOffset = EditorGUI.FloatField(new Rect(rect.x + 5f + width * 1, rect.y + 20, width - 5f, 18), new GUIContent("OY:"), m_yOffset);
  242. if (GUI.changed) UpdateGlobalProperty("yOffset", m_yOffset);
  243. m_xAdvance = EditorGUI.FloatField(new Rect(rect.x + 5f + width * 2, rect.y + 20, width - 5f, 18), new GUIContent("ADV."), m_xAdvance);
  244. if (GUI.changed) UpdateGlobalProperty("xAdvance", m_xAdvance);
  245. m_scale = EditorGUI.FloatField(new Rect(rect.x + 5f + width * 3, rect.y + 20, width - 5f, 18), new GUIContent("SF."), m_scale);
  246. if (GUI.changed) UpdateGlobalProperty("scale", m_scale);
  247. EditorGUILayout.EndVertical();
  248. #endregion
  249. GUI.changed = old_ChangedState;
  250. }
  251. if (serializedObject.ApplyModifiedProperties() || evt_cmd == k_UndoRedo || isAssetDirty)
  252. {
  253. isAssetDirty = false;
  254. EditorUtility.SetDirty(target);
  255. //TMPro_EditorUtility.RepaintAll(); // Consider SetDirty
  256. }
  257. // Clear selection if mouse event was not consumed.
  258. GUI.enabled = true;
  259. if (currentEvent.type == EventType.MouseDown && currentEvent.button == 0)
  260. m_selectedElement = -1;
  261. }
  262. /// <summary>
  263. ///
  264. /// </summary>
  265. /// <param name="arraySize"></param>
  266. /// <param name="itemsPerPage"></param>
  267. void DisplayGlyphPageNavigation(int arraySize, int itemsPerPage)
  268. {
  269. Rect pagePos = EditorGUILayout.GetControlRect(false, 20);
  270. pagePos.width /= 3;
  271. int shiftMultiplier = Event.current.shift ? 10 : 1; // Page + Shift goes 10 page forward
  272. // Previous Page
  273. GUI.enabled = m_page > 0;
  274. if (GUI.Button(pagePos, "Previous Page"))
  275. {
  276. m_page -= 1 * shiftMultiplier;
  277. //m_isNewPage = true;
  278. }
  279. // Page Counter
  280. GUI.enabled = true;
  281. pagePos.x += pagePos.width;
  282. int totalPages = (int)(arraySize / (float)itemsPerPage + 0.999f);
  283. GUI.Label(pagePos, "Page " + (m_page + 1) + " / " + totalPages, TMP_UIStyleManager.centeredLabel);
  284. // Next Page
  285. pagePos.x += pagePos.width;
  286. GUI.enabled = itemsPerPage * (m_page + 1) < arraySize;
  287. if (GUI.Button(pagePos, "Next Page"))
  288. {
  289. m_page += 1 * shiftMultiplier;
  290. //m_isNewPage = true;
  291. }
  292. // Clamp page range
  293. m_page = Mathf.Clamp(m_page, 0, arraySize / itemsPerPage);
  294. GUI.enabled = true;
  295. }
  296. /// <summary>
  297. /// Method to update the properties of all sprites
  298. /// </summary>
  299. /// <param name="property"></param>
  300. /// <param name="value"></param>
  301. void UpdateGlobalProperty(string property, float value)
  302. {
  303. int arraySize = m_spriteInfoList_prop.arraySize;
  304. for (int i = 0; i < arraySize; i++)
  305. {
  306. SerializedProperty spriteInfo = m_spriteInfoList_prop.GetArrayElementAtIndex(i);
  307. spriteInfo.FindPropertyRelative(property).floatValue = value;
  308. }
  309. GUI.changed = false;
  310. }
  311. // Check if any of the Style elements were clicked on.
  312. private bool DoSelectionCheck(Rect selectionArea)
  313. {
  314. Event currentEvent = Event.current;
  315. switch (currentEvent.type)
  316. {
  317. case EventType.MouseDown:
  318. if (selectionArea.Contains(currentEvent.mousePosition) && currentEvent.button == 0)
  319. {
  320. currentEvent.Use();
  321. return true;
  322. }
  323. break;
  324. }
  325. return false;
  326. }
  327. /// <summary>
  328. /// Swap the sprite item at the currently selected array index to another index.
  329. /// </summary>
  330. /// <param name="selectedIndex">Selected index.</param>
  331. /// <param name="newIndex">New index.</param>
  332. void SwapSpriteElement(int selectedIndex, int newIndex)
  333. {
  334. m_spriteInfoList_prop.MoveArrayElement(selectedIndex, newIndex);
  335. m_spriteInfoList_prop.GetArrayElementAtIndex(selectedIndex).FindPropertyRelative("id").intValue = selectedIndex;
  336. m_spriteInfoList_prop.GetArrayElementAtIndex(newIndex).FindPropertyRelative("id").intValue = newIndex;
  337. m_selectedElement = newIndex;
  338. m_isSearchDirty = true;
  339. }
  340. /// <summary>
  341. /// Move Sprite Element at selected index to another index and reorder sprite list.
  342. /// </summary>
  343. /// <param name="selectedIndex"></param>
  344. /// <param name="newIndex"></param>
  345. void MoveSpriteElement(int selectedIndex, int newIndex)
  346. {
  347. m_spriteInfoList_prop.MoveArrayElement(selectedIndex, newIndex);
  348. // Reorder Sprite Element Index
  349. for (int i = 0; i < m_spriteInfoList_prop.arraySize; i++)
  350. m_spriteInfoList_prop.GetArrayElementAtIndex(i).FindPropertyRelative("id").intValue = i;
  351. m_selectedElement = newIndex;
  352. m_isSearchDirty = true;
  353. }
  354. /// <summary>
  355. ///
  356. /// </summary>
  357. /// <param name="source"></param>
  358. /// <param name="target"></param>
  359. void CopySerializedProperty(SerializedProperty source, ref SerializedProperty target)
  360. {
  361. target.FindPropertyRelative("name").stringValue = source.FindPropertyRelative("name").stringValue;
  362. target.FindPropertyRelative("hashCode").intValue = source.FindPropertyRelative("hashCode").intValue;
  363. target.FindPropertyRelative("x").floatValue = source.FindPropertyRelative("x").floatValue;
  364. target.FindPropertyRelative("y").floatValue = source.FindPropertyRelative("y").floatValue;
  365. target.FindPropertyRelative("width").floatValue = source.FindPropertyRelative("width").floatValue;
  366. target.FindPropertyRelative("height").floatValue = source.FindPropertyRelative("height").floatValue;
  367. target.FindPropertyRelative("xOffset").floatValue = source.FindPropertyRelative("xOffset").floatValue;
  368. target.FindPropertyRelative("yOffset").floatValue = source.FindPropertyRelative("yOffset").floatValue;
  369. target.FindPropertyRelative("xAdvance").floatValue = source.FindPropertyRelative("xAdvance").floatValue;
  370. target.FindPropertyRelative("scale").floatValue = source.FindPropertyRelative("scale").floatValue;
  371. target.FindPropertyRelative("sprite").objectReferenceValue = source.FindPropertyRelative("sprite").objectReferenceValue;
  372. }
  373. /// <summary>
  374. ///
  375. /// </summary>
  376. /// <param name="searchPattern"></param>
  377. /// <returns></returns>
  378. void SearchGlyphTable(string searchPattern, ref List<int> searchResults)
  379. {
  380. if (searchResults == null) searchResults = new List<int>();
  381. searchResults.Clear();
  382. int arraySize = m_spriteInfoList_prop.arraySize;
  383. for (int i = 0; i < arraySize; i++)
  384. {
  385. SerializedProperty sourceSprite = m_spriteInfoList_prop.GetArrayElementAtIndex(i);
  386. // Check for potential match against decimal id
  387. int id = sourceSprite.FindPropertyRelative("id").intValue;
  388. if (id.ToString().Contains(searchPattern))
  389. searchResults.Add(i);
  390. // Check for potential match against name
  391. string name = sourceSprite.FindPropertyRelative("name").stringValue.ToLower(System.Globalization.CultureInfo.InvariantCulture).Trim();
  392. if (name.Contains(searchPattern))
  393. searchResults.Add(i);
  394. }
  395. }
  396. }
  397. }