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.

1006 lines
43 KiB

  1. using UnityEngine;
  2. using UnityEditor;
  3. using UnityEditorInternal;
  4. using System.Collections;
  5. using System.Collections.Generic;
  6. namespace TMPro.EditorUtilities
  7. {
  8. [CustomPropertyDrawer(typeof(TMP_FontWeights))]
  9. public class FontWeightDrawer : PropertyDrawer
  10. {
  11. public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
  12. {
  13. SerializedProperty prop_regular = property.FindPropertyRelative("regularTypeface");
  14. SerializedProperty prop_italic = property.FindPropertyRelative("italicTypeface");
  15. float width = position.width;
  16. position.width = EditorGUIUtility.labelWidth;
  17. EditorGUI.LabelField(position, label);
  18. int oldIndent = EditorGUI.indentLevel;
  19. EditorGUI.indentLevel = 0;
  20. // NORMAL FACETYPE
  21. if (label.text[0] == '4') GUI.enabled = false;
  22. position.x += position.width; position.width = (width - position.width) / 2;
  23. EditorGUI.PropertyField(position, prop_regular, GUIContent.none);
  24. // ITALIC FACETYPE
  25. GUI.enabled = true;
  26. position.x += position.width;
  27. EditorGUI.PropertyField(position, prop_italic, GUIContent.none);
  28. EditorGUI.indentLevel = oldIndent;
  29. }
  30. }
  31. [CustomEditor(typeof(TMP_FontAsset))]
  32. public class TMP_FontAssetEditor : Editor
  33. {
  34. private struct UI_PanelState
  35. {
  36. public static bool fontSubAssetsPanel = true;
  37. public static bool fontWeightPanel = true;
  38. public static bool fallbackFontAssetPanel = true;
  39. public static bool glyphInfoPanel = false;
  40. public static bool kerningInfoPanel = false;
  41. }
  42. private struct Warning
  43. {
  44. public bool isEnabled;
  45. public double expirationTime;
  46. }
  47. private int m_CurrentGlyphPage = 0;
  48. private int m_CurrentKerningPage = 0;
  49. private int m_SelectedGlyphRecord = -1;
  50. private int m_SelectedAdjustmentRecord = -1;
  51. private string m_dstGlyphID;
  52. private const string k_placeholderUnicodeHex = "<i>Unicode Hex ID</i>";
  53. private string m_unicodeHexLabel = k_placeholderUnicodeHex;
  54. private Warning m_AddGlyphWarning;
  55. private string m_GlyphSearchPattern;
  56. private List<int> m_GlyphSearchList;
  57. private string m_KerningTableSearchPattern;
  58. private List<int> m_KerningTableSearchList;
  59. private bool m_isSearchDirty;
  60. private const string k_UndoRedo = "UndoRedoPerformed";
  61. private SerializedProperty font_atlas_prop;
  62. private SerializedProperty font_material_prop;
  63. private SerializedProperty fontWeights_prop;
  64. //private SerializedProperty fallbackFontAssets_prop;
  65. private ReorderableList m_list;
  66. private SerializedProperty font_normalStyle_prop;
  67. private SerializedProperty font_normalSpacing_prop;
  68. private SerializedProperty font_boldStyle_prop;
  69. private SerializedProperty font_boldSpacing_prop;
  70. private SerializedProperty font_italicStyle_prop;
  71. private SerializedProperty font_tabSize_prop;
  72. private SerializedProperty m_fontInfo_prop;
  73. private SerializedProperty m_glyphInfoList_prop;
  74. private SerializedProperty m_kerningInfo_prop;
  75. private KerningTable m_kerningTable;
  76. private SerializedProperty m_kerningPairs_prop;
  77. private SerializedProperty m_kerningPair_prop;
  78. private TMP_FontAsset m_fontAsset;
  79. private Material[] m_materialPresets;
  80. private bool isAssetDirty = false;
  81. private int errorCode;
  82. private System.DateTime timeStamp;
  83. public void OnEnable()
  84. {
  85. font_atlas_prop = serializedObject.FindProperty("atlas");
  86. font_material_prop = serializedObject.FindProperty("material");
  87. fontWeights_prop = serializedObject.FindProperty("fontWeights");
  88. m_list = new ReorderableList(serializedObject, serializedObject.FindProperty("fallbackFontAssets"), true, true, true, true);
  89. m_list.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
  90. {
  91. var element = m_list.serializedProperty.GetArrayElementAtIndex(index);
  92. rect.y += 2;
  93. EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), element, GUIContent.none);
  94. };
  95. m_list.drawHeaderCallback = rect =>
  96. {
  97. EditorGUI.LabelField(rect, "Fallback Font Asset List");
  98. };
  99. font_normalStyle_prop = serializedObject.FindProperty("normalStyle");
  100. font_normalSpacing_prop = serializedObject.FindProperty("normalSpacingOffset");
  101. font_boldStyle_prop = serializedObject.FindProperty("boldStyle");
  102. font_boldSpacing_prop = serializedObject.FindProperty("boldSpacing");
  103. font_italicStyle_prop = serializedObject.FindProperty("italicStyle");
  104. font_tabSize_prop = serializedObject.FindProperty("tabSize");
  105. m_fontInfo_prop = serializedObject.FindProperty("m_fontInfo");
  106. m_glyphInfoList_prop = serializedObject.FindProperty("m_glyphInfoList");
  107. m_kerningInfo_prop = serializedObject.FindProperty("m_kerningInfo");
  108. m_kerningPair_prop = serializedObject.FindProperty("m_kerningPair");
  109. m_kerningPairs_prop = m_kerningInfo_prop.FindPropertyRelative("kerningPairs");
  110. m_fontAsset = target as TMP_FontAsset;
  111. m_kerningTable = m_fontAsset.kerningInfo;
  112. m_materialPresets = TMP_EditorUtility.FindMaterialReferences(m_fontAsset);
  113. m_GlyphSearchList = new List<int>();
  114. m_KerningTableSearchList = new List<int>();
  115. }
  116. public override void OnInspectorGUI()
  117. {
  118. // Check Warnings
  119. //Debug.Log("OnInspectorGUI Called.");
  120. Event currentEvent = Event.current;
  121. serializedObject.Update();
  122. // TextMeshPro Font Info Panel
  123. Rect rect = EditorGUILayout.GetControlRect();
  124. GUI.Label(rect, "Face Info", EditorStyles.boldLabel);
  125. rect.x += rect.width - 130f;
  126. rect.width = 130f;
  127. if (GUI.Button(rect, "Update Atlas Texture"))
  128. {
  129. TMPro_FontAssetCreatorWindow.ShowFontAtlasCreatorWindow(target as TMP_FontAsset);
  130. }
  131. EditorGUI.indentLevel = 1;
  132. GUI.enabled = false; // Lock UI
  133. float labelWidth = EditorGUIUtility.labelWidth;
  134. float fieldWidth = EditorGUIUtility.fieldWidth;
  135. EditorGUILayout.PropertyField(m_fontInfo_prop.FindPropertyRelative("Name"), new GUIContent("Font Source"));
  136. EditorGUILayout.PropertyField(m_fontInfo_prop.FindPropertyRelative("PointSize"));
  137. GUI.enabled = true;
  138. EditorGUILayout.PropertyField(m_fontInfo_prop.FindPropertyRelative("Scale"));
  139. EditorGUILayout.PropertyField(m_fontInfo_prop.FindPropertyRelative("LineHeight"));
  140. EditorGUILayout.PropertyField(m_fontInfo_prop.FindPropertyRelative("Ascender"));
  141. EditorGUILayout.PropertyField(m_fontInfo_prop.FindPropertyRelative("CapHeight"));
  142. EditorGUILayout.PropertyField(m_fontInfo_prop.FindPropertyRelative("Baseline"));
  143. EditorGUILayout.PropertyField(m_fontInfo_prop.FindPropertyRelative("Descender"));
  144. EditorGUILayout.PropertyField(m_fontInfo_prop.FindPropertyRelative("Underline"), new GUIContent("Underline Offset"));
  145. EditorGUILayout.PropertyField(m_fontInfo_prop.FindPropertyRelative("strikethrough"), new GUIContent("Strikethrough Offset"));
  146. EditorGUILayout.PropertyField(m_fontInfo_prop.FindPropertyRelative("SuperscriptOffset"));
  147. EditorGUILayout.PropertyField(m_fontInfo_prop.FindPropertyRelative("SubscriptOffset"));
  148. SerializedProperty subSize_prop = m_fontInfo_prop.FindPropertyRelative("SubSize");
  149. EditorGUILayout.PropertyField(subSize_prop, new GUIContent("Super / Subscript Size"));
  150. subSize_prop.floatValue = Mathf.Clamp(subSize_prop.floatValue, 0.25f, 1f);
  151. GUI.enabled = false;
  152. //EditorGUILayout.PropertyField(m_fontInfo_prop.FindPropertyRelative("Padding"));
  153. //GUILayout.label("Atlas Size");
  154. EditorGUI.indentLevel = 1;
  155. EditorGUILayout.Space();
  156. EditorGUILayout.PropertyField(m_fontInfo_prop.FindPropertyRelative("Padding"));
  157. EditorGUILayout.PropertyField(m_fontInfo_prop.FindPropertyRelative("AtlasWidth"), new GUIContent("Width"));
  158. EditorGUILayout.PropertyField(m_fontInfo_prop.FindPropertyRelative("AtlasHeight"), new GUIContent("Height"));
  159. GUI.enabled = true;
  160. EditorGUILayout.Space();
  161. EditorGUI.indentLevel = 0;
  162. UI_PanelState.fontSubAssetsPanel = EditorGUILayout.Foldout(UI_PanelState.fontSubAssetsPanel, new GUIContent("Font Sub-Assets"), true, TMP_UIStyleManager.boldFoldout);
  163. if (UI_PanelState.fontSubAssetsPanel)
  164. {
  165. GUI.enabled = false;
  166. EditorGUI.indentLevel = 1;
  167. EditorGUILayout.PropertyField(font_atlas_prop, new GUIContent("Font Atlas"));
  168. EditorGUILayout.PropertyField(font_material_prop, new GUIContent("Font Material"));
  169. GUI.enabled = true;
  170. EditorGUILayout.Space();
  171. }
  172. string evt_cmd = Event.current.commandName; // Get Current Event CommandName to check for Undo Events
  173. // FONT SETTINGS
  174. EditorGUI.indentLevel = 0;
  175. UI_PanelState.fontWeightPanel = EditorGUILayout.Foldout(UI_PanelState.fontWeightPanel, new GUIContent("Font Weights", "The Font Assets that will be used for different font weights and the settings used to simulate a typeface when no asset is available."), true, TMP_UIStyleManager.boldFoldout);
  176. if (UI_PanelState.fontWeightPanel)
  177. {
  178. EditorGUIUtility.labelWidth *= 0.75f;
  179. EditorGUIUtility.fieldWidth *= 0.25f;
  180. EditorGUILayout.BeginVertical();
  181. EditorGUI.indentLevel = 1;
  182. rect = EditorGUILayout.GetControlRect(true);
  183. rect.x += EditorGUIUtility.labelWidth;
  184. rect.width = (rect.width - EditorGUIUtility.labelWidth) / 2f;
  185. GUI.Label(rect, "Normal Style", EditorStyles.boldLabel);
  186. rect.x += rect.width;
  187. GUI.Label(rect, "Italic Style", EditorStyles.boldLabel);
  188. EditorGUI.indentLevel = 1;
  189. //EditorGUILayout.PropertyField(fontWeights_prop.GetArrayElementAtIndex(1), new GUIContent("100 - Thin"));
  190. //EditorGUILayout.PropertyField(fontWeights_prop.GetArrayElementAtIndex(2), new GUIContent("200 - Extra-Light"));
  191. //EditorGUILayout.PropertyField(fontWeights_prop.GetArrayElementAtIndex(3), new GUIContent("300 - Light"));
  192. EditorGUILayout.PropertyField(fontWeights_prop.GetArrayElementAtIndex(4), new GUIContent("400 - Regular"));
  193. //EditorGUILayout.PropertyField(fontWeights_prop.GetArrayElementAtIndex(5), new GUIContent("500 - Medium"));
  194. //EditorGUILayout.PropertyField(fontWeights_prop.GetArrayElementAtIndex(6), new GUIContent("600 - Demi-Bold"));
  195. EditorGUILayout.PropertyField(fontWeights_prop.GetArrayElementAtIndex(7), new GUIContent("700 - Bold"));
  196. //EditorGUILayout.PropertyField(fontWeights_prop.GetArrayElementAtIndex(8), new GUIContent("800 - Heavy"));
  197. //EditorGUILayout.PropertyField(fontWeights_prop.GetArrayElementAtIndex(9), new GUIContent("900 - Black"));
  198. EditorGUILayout.EndVertical();
  199. EditorGUILayout.Space();
  200. EditorGUILayout.BeginVertical();
  201. EditorGUILayout.BeginHorizontal();
  202. EditorGUILayout.PropertyField(font_normalStyle_prop, new GUIContent("Normal Weight"));
  203. font_normalStyle_prop.floatValue = Mathf.Clamp(font_normalStyle_prop.floatValue, -3.0f, 3.0f);
  204. if (GUI.changed || evt_cmd == k_UndoRedo)
  205. {
  206. GUI.changed = false;
  207. // Modify the material property on matching material presets.
  208. for (int i = 0; i < m_materialPresets.Length; i++)
  209. m_materialPresets[i].SetFloat("_WeightNormal", font_normalStyle_prop.floatValue);
  210. }
  211. EditorGUILayout.PropertyField(font_boldStyle_prop, new GUIContent("Bold Weight"));
  212. font_boldStyle_prop.floatValue = Mathf.Clamp(font_boldStyle_prop.floatValue, -3.0f, 3.0f);
  213. if (GUI.changed || evt_cmd == k_UndoRedo)
  214. {
  215. GUI.changed = false;
  216. // Modify the material property on matching material presets.
  217. for (int i = 0; i < m_materialPresets.Length; i++)
  218. m_materialPresets[i].SetFloat("_WeightBold", font_boldStyle_prop.floatValue);
  219. }
  220. EditorGUILayout.EndHorizontal();
  221. EditorGUILayout.BeginHorizontal();
  222. EditorGUILayout.PropertyField(font_normalSpacing_prop, new GUIContent("Spacing Offset"));
  223. font_normalSpacing_prop.floatValue = Mathf.Clamp(font_normalSpacing_prop.floatValue, -100, 100);
  224. if (GUI.changed || evt_cmd == k_UndoRedo)
  225. {
  226. GUI.changed = false;
  227. }
  228. EditorGUILayout.PropertyField(font_boldSpacing_prop, new GUIContent("Bold Spacing"));
  229. font_boldSpacing_prop.floatValue = Mathf.Clamp(font_boldSpacing_prop.floatValue, 0, 100);
  230. if (GUI.changed || evt_cmd == k_UndoRedo)
  231. {
  232. GUI.changed = false;
  233. }
  234. EditorGUILayout.EndHorizontal();
  235. EditorGUILayout.BeginHorizontal();
  236. EditorGUILayout.PropertyField(font_italicStyle_prop, new GUIContent("Italic Style"));
  237. font_italicStyle_prop.intValue = Mathf.Clamp(font_italicStyle_prop.intValue, 15, 60);
  238. EditorGUILayout.PropertyField(font_tabSize_prop, new GUIContent("Tab Multiple"));
  239. EditorGUILayout.EndHorizontal();
  240. EditorGUILayout.EndVertical();
  241. EditorGUILayout.Space();
  242. }
  243. EditorGUIUtility.labelWidth = 0;
  244. EditorGUIUtility.fieldWidth = 0;
  245. // FALLBACK FONT ASSETS
  246. EditorGUI.indentLevel = 0;
  247. UI_PanelState.fallbackFontAssetPanel = EditorGUILayout.Foldout(UI_PanelState.fallbackFontAssetPanel, new GUIContent("Fallback Font Assets", "Select the Font Assets that will be searched and used as fallback when characters are missing from this font asset."), true, TMP_UIStyleManager.boldFoldout);
  248. if (UI_PanelState.fallbackFontAssetPanel)
  249. {
  250. EditorGUIUtility.labelWidth = 120;
  251. EditorGUI.indentLevel = 0;
  252. m_list.DoLayoutList();
  253. EditorGUILayout.Space();
  254. }
  255. // GLYPH INFO TABLE
  256. #region Glyph Table
  257. EditorGUIUtility.labelWidth = labelWidth;
  258. EditorGUIUtility.fieldWidth = fieldWidth;
  259. EditorGUI.indentLevel = 0;
  260. UI_PanelState.glyphInfoPanel = EditorGUILayout.Foldout(UI_PanelState.glyphInfoPanel, new GUIContent("Glyph Table"), true, TMP_UIStyleManager.boldFoldout);
  261. if (UI_PanelState.glyphInfoPanel)
  262. {
  263. int arraySize = m_glyphInfoList_prop.arraySize;
  264. int itemsPerPage = 15;
  265. // Display Glyph Management Tools
  266. EditorGUILayout.BeginVertical(EditorStyles.helpBox);
  267. {
  268. // Search Bar implementation
  269. #region DISPLAY SEARCH BAR
  270. EditorGUILayout.BeginHorizontal();
  271. {
  272. EditorGUIUtility.labelWidth = 130f;
  273. EditorGUI.BeginChangeCheck();
  274. string searchPattern = EditorGUILayout.TextField("Glyph Search", m_GlyphSearchPattern, "SearchTextField");
  275. if (EditorGUI.EndChangeCheck() || m_isSearchDirty)
  276. {
  277. if (string.IsNullOrEmpty(searchPattern) == false)
  278. {
  279. m_GlyphSearchPattern = searchPattern;
  280. // Search Glyph Table for potential matches
  281. SearchGlyphTable(m_GlyphSearchPattern, ref m_GlyphSearchList);
  282. }
  283. else
  284. m_GlyphSearchPattern = null;
  285. m_isSearchDirty = false;
  286. }
  287. string styleName = string.IsNullOrEmpty(m_GlyphSearchPattern) ? "SearchCancelButtonEmpty" : "SearchCancelButton";
  288. if (GUILayout.Button(GUIContent.none, styleName))
  289. {
  290. GUIUtility.keyboardControl = 0;
  291. m_GlyphSearchPattern = string.Empty;
  292. }
  293. }
  294. EditorGUILayout.EndHorizontal();
  295. #endregion
  296. // Display Page Navigation
  297. if (!string.IsNullOrEmpty(m_GlyphSearchPattern))
  298. arraySize = m_GlyphSearchList.Count;
  299. DisplayPageNavigation(ref m_CurrentGlyphPage, arraySize, itemsPerPage);
  300. }
  301. EditorGUILayout.EndVertical();
  302. // Display Glyph Table Elements
  303. if (arraySize > 0)
  304. {
  305. // Display each GlyphInfo entry using the GlyphInfo property drawer.
  306. for (int i = itemsPerPage * m_CurrentGlyphPage; i < arraySize && i < itemsPerPage * (m_CurrentGlyphPage + 1); i++)
  307. {
  308. // Define the start of the selection region of the element.
  309. Rect elementStartRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true));
  310. int elementIndex = i;
  311. if (!string.IsNullOrEmpty(m_GlyphSearchPattern))
  312. elementIndex = m_GlyphSearchList[i];
  313. SerializedProperty glyphInfo = m_glyphInfoList_prop.GetArrayElementAtIndex(elementIndex);
  314. EditorGUILayout.BeginVertical(EditorStyles.helpBox);
  315. EditorGUI.BeginDisabledGroup(i != m_SelectedGlyphRecord);
  316. {
  317. EditorGUILayout.PropertyField(glyphInfo);
  318. }
  319. EditorGUI.EndDisabledGroup();
  320. EditorGUILayout.EndVertical();
  321. // Define the end of the selection region of the element.
  322. Rect elementEndRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true));
  323. // Check for Item selection
  324. Rect selectionArea = new Rect(elementStartRegion.x, elementStartRegion.y, elementEndRegion.width, elementEndRegion.y - elementStartRegion.y);
  325. if (DoSelectionCheck(selectionArea))
  326. {
  327. if (m_SelectedGlyphRecord == i)
  328. m_SelectedGlyphRecord = -1;
  329. else
  330. {
  331. m_SelectedGlyphRecord = i;
  332. m_AddGlyphWarning.isEnabled = false;
  333. m_unicodeHexLabel = k_placeholderUnicodeHex;
  334. GUIUtility.keyboardControl = 0;
  335. }
  336. }
  337. // Draw Selection Highlight and Glyph Options
  338. if (m_SelectedGlyphRecord == i)
  339. {
  340. TMP_EditorUtility.DrawBox(selectionArea, 2f, new Color32(40, 192, 255, 255));
  341. // Draw Glyph management options
  342. Rect controlRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight * 1f);
  343. float optionAreaWidth = controlRect.width * 0.6f;
  344. float btnWidth = optionAreaWidth / 3;
  345. Rect position = new Rect(controlRect.x + controlRect.width * .4f, controlRect.y, btnWidth, controlRect.height);
  346. // Copy Selected Glyph to Target Glyph ID
  347. GUI.enabled = !string.IsNullOrEmpty(m_dstGlyphID);
  348. if (GUI.Button(position, new GUIContent("Copy to")))
  349. {
  350. GUIUtility.keyboardControl = 0;
  351. // Convert Hex Value to Decimal
  352. int dstGlyphID = TMP_TextUtilities.StringToInt(m_dstGlyphID);
  353. //Add new glyph at target Unicode hex id.
  354. if (!AddNewGlyph(elementIndex, dstGlyphID))
  355. {
  356. m_AddGlyphWarning.isEnabled = true;
  357. m_AddGlyphWarning.expirationTime = EditorApplication.timeSinceStartup + 1;
  358. }
  359. m_dstGlyphID = string.Empty;
  360. m_isSearchDirty = true;
  361. TMPro_EventManager.ON_FONT_PROPERTY_CHANGED(true, m_fontAsset);
  362. }
  363. // Target Glyph ID
  364. GUI.enabled = true;
  365. position.x += btnWidth;
  366. GUI.SetNextControlName("GlyphID_Input");
  367. m_dstGlyphID = EditorGUI.TextField(position, m_dstGlyphID);
  368. // Placeholder text
  369. EditorGUI.LabelField(position, new GUIContent(m_unicodeHexLabel, "The Unicode (Hex) ID of the duplicated Glyph"), TMP_UIStyleManager.label);
  370. // Only filter the input when the destination glyph ID text field has focus.
  371. if (GUI.GetNameOfFocusedControl() == "GlyphID_Input")
  372. {
  373. m_unicodeHexLabel = string.Empty;
  374. //Filter out unwanted characters.
  375. char chr = Event.current.character;
  376. if ((chr < '0' || chr > '9') && (chr < 'a' || chr > 'f') && (chr < 'A' || chr > 'F'))
  377. {
  378. Event.current.character = '\0';
  379. }
  380. }
  381. else
  382. m_unicodeHexLabel = k_placeholderUnicodeHex;
  383. // Remove Glyph
  384. position.x += btnWidth;
  385. if (GUI.Button(position, "Remove"))
  386. {
  387. GUIUtility.keyboardControl = 0;
  388. RemoveGlyphFromList(elementIndex);
  389. isAssetDirty = true;
  390. m_SelectedGlyphRecord = -1;
  391. m_isSearchDirty = true;
  392. break;
  393. }
  394. if (m_AddGlyphWarning.isEnabled && EditorApplication.timeSinceStartup < m_AddGlyphWarning.expirationTime)
  395. {
  396. EditorGUILayout.HelpBox("The Destination Glyph ID already exists", MessageType.Warning);
  397. }
  398. }
  399. }
  400. }
  401. DisplayPageNavigation(ref m_CurrentGlyphPage, arraySize, itemsPerPage);
  402. EditorGUILayout.Space();
  403. }
  404. #endregion
  405. // KERNING TABLE PANEL
  406. #region Kerning Table
  407. EditorGUIUtility.labelWidth = labelWidth;
  408. EditorGUIUtility.fieldWidth = fieldWidth;
  409. EditorGUI.indentLevel = 0;
  410. UI_PanelState.kerningInfoPanel = EditorGUILayout.Foldout(UI_PanelState.kerningInfoPanel, new GUIContent("Glyph Adjustment Table"), true, TMP_UIStyleManager.boldFoldout);
  411. if (UI_PanelState.kerningInfoPanel)
  412. {
  413. int arraySize = m_kerningPairs_prop.arraySize;
  414. int itemsPerPage = 20;
  415. // Display Kerning Pair Management Tools
  416. EditorGUILayout.BeginVertical(EditorStyles.helpBox);
  417. {
  418. // Search Bar implementation
  419. #region DISPLAY SEARCH BAR
  420. EditorGUILayout.BeginHorizontal();
  421. {
  422. EditorGUIUtility.labelWidth = 150f;
  423. EditorGUI.BeginChangeCheck();
  424. string searchPattern = EditorGUILayout.TextField("Adjustment Pair Search", m_KerningTableSearchPattern, "SearchTextField");
  425. if (EditorGUI.EndChangeCheck() || m_isSearchDirty)
  426. {
  427. if (string.IsNullOrEmpty(searchPattern) == false)
  428. {
  429. m_KerningTableSearchPattern = searchPattern;
  430. // Search Glyph Table for potential matches
  431. SearchKerningTable(m_KerningTableSearchPattern, ref m_KerningTableSearchList);
  432. }
  433. else
  434. m_KerningTableSearchPattern = null;
  435. m_isSearchDirty = false;
  436. }
  437. string styleName = string.IsNullOrEmpty(m_KerningTableSearchPattern) ? "SearchCancelButtonEmpty" : "SearchCancelButton";
  438. if (GUILayout.Button(GUIContent.none, styleName))
  439. {
  440. GUIUtility.keyboardControl = 0;
  441. m_KerningTableSearchPattern = string.Empty;
  442. }
  443. }
  444. EditorGUILayout.EndHorizontal();
  445. #endregion
  446. // Display Page Navigation
  447. if (!string.IsNullOrEmpty(m_KerningTableSearchPattern))
  448. arraySize = m_KerningTableSearchList.Count;
  449. DisplayPageNavigation(ref m_CurrentKerningPage, arraySize, itemsPerPage);
  450. }
  451. EditorGUILayout.EndVertical();
  452. //Rect pos;
  453. //pos = EditorGUILayout.GetControlRect(false, 20);
  454. //pos.x += 5;
  455. //EditorGUI.LabelField(pos, "First Glyph", TMP_UIStyleManager.TMP_GUISkin.label);
  456. //pos.x += 100;
  457. //EditorGUI.LabelField(pos, "Adjustment Values", TMP_UIStyleManager.TMP_GUISkin.label);
  458. //pos.x = pos.width / 2 + 5;
  459. //EditorGUI.LabelField(pos, "Second Glyph", TMP_UIStyleManager.TMP_GUISkin.label);
  460. //pos.x += 100;
  461. //EditorGUI.LabelField(pos, "Adjustment Values", TMP_UIStyleManager.TMP_GUISkin.label);
  462. if (arraySize > 0)
  463. {
  464. // Display each GlyphInfo entry using the GlyphInfo property drawer.
  465. for (int i = itemsPerPage * m_CurrentKerningPage; i < arraySize && i < itemsPerPage * (m_CurrentKerningPage + 1); i++)
  466. {
  467. // Define the start of the selection region of the element.
  468. Rect elementStartRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true));
  469. int elementIndex = i;
  470. if (!string.IsNullOrEmpty(m_KerningTableSearchPattern))
  471. elementIndex = m_KerningTableSearchList[i];
  472. SerializedProperty kerningInfo = m_kerningPairs_prop.GetArrayElementAtIndex(elementIndex);
  473. EditorGUILayout.BeginVertical(EditorStyles.helpBox);
  474. EditorGUI.BeginDisabledGroup(i != m_SelectedAdjustmentRecord);
  475. {
  476. EditorGUILayout.PropertyField(kerningInfo, new GUIContent("Selectable"));
  477. }
  478. EditorGUI.EndDisabledGroup();
  479. EditorGUILayout.EndVertical();
  480. // Define the end of the selection region of the element.
  481. Rect elementEndRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true));
  482. // Check for Item selection
  483. Rect selectionArea = new Rect(elementStartRegion.x, elementStartRegion.y, elementEndRegion.width, elementEndRegion.y - elementStartRegion.y);
  484. if (DoSelectionCheck(selectionArea))
  485. {
  486. if (m_SelectedAdjustmentRecord == i)
  487. {
  488. m_SelectedAdjustmentRecord = -1;
  489. }
  490. else
  491. {
  492. m_SelectedAdjustmentRecord = i;
  493. GUIUtility.keyboardControl = 0;
  494. }
  495. }
  496. // Draw Selection Highlight and Kerning Pair Options
  497. if (m_SelectedAdjustmentRecord == i)
  498. {
  499. TMP_EditorUtility.DrawBox(selectionArea, 2f, new Color32(40, 192, 255, 255));
  500. // Draw Glyph management options
  501. Rect controlRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight * 1f);
  502. float optionAreaWidth = controlRect.width;
  503. float btnWidth = optionAreaWidth / 4;
  504. Rect position = new Rect(controlRect.x + controlRect.width - btnWidth, controlRect.y, btnWidth, controlRect.height);
  505. // Remove Kerning pair
  506. GUI.enabled = true;
  507. if (GUI.Button(position, "Remove"))
  508. {
  509. GUIUtility.keyboardControl = 0;
  510. m_kerningTable.RemoveKerningPair(i);
  511. m_fontAsset.ReadFontDefinition();
  512. isAssetDirty = true;
  513. m_SelectedAdjustmentRecord = -1;
  514. m_isSearchDirty = true;
  515. break;
  516. }
  517. }
  518. }
  519. }
  520. DisplayPageNavigation(ref m_CurrentKerningPage, arraySize, itemsPerPage);
  521. GUILayout.Space(5);
  522. // Add new kerning pair
  523. EditorGUILayout.BeginVertical(EditorStyles.helpBox);
  524. {
  525. EditorGUILayout.PropertyField(m_kerningPair_prop);
  526. }
  527. EditorGUILayout.EndVertical();
  528. if (GUILayout.Button("Add New Kerning Pair"))
  529. {
  530. int firstGlyph = m_kerningPair_prop.FindPropertyRelative("m_FirstGlyph").intValue;
  531. int secondGlyph = m_kerningPair_prop.FindPropertyRelative("m_SecondGlyph").intValue;
  532. GlyphValueRecord firstGlyphAdjustments = GetGlyphAdjustments(m_kerningPair_prop.FindPropertyRelative("m_FirstGlyphAdjustments"));
  533. GlyphValueRecord secondGlyphAdjustments = GetGlyphAdjustments(m_kerningPair_prop.FindPropertyRelative("m_SecondGlyphAdjustments"));
  534. errorCode = m_kerningTable.AddGlyphPairAdjustmentRecord((uint)firstGlyph, firstGlyphAdjustments, (uint)secondGlyph, secondGlyphAdjustments);
  535. // Sort Kerning Pairs & Reload Font Asset if new kerning pair was added.
  536. if (errorCode != -1)
  537. {
  538. m_kerningTable.SortKerningPairs();
  539. m_fontAsset.ReadFontDefinition();
  540. serializedObject.ApplyModifiedProperties();
  541. isAssetDirty = true;
  542. m_isSearchDirty = true;
  543. }
  544. else
  545. {
  546. timeStamp = System.DateTime.Now.AddSeconds(5);
  547. }
  548. // Clear Add Kerning Pair Panel
  549. // TODO
  550. }
  551. if (errorCode == -1)
  552. {
  553. GUILayout.BeginHorizontal();
  554. GUILayout.FlexibleSpace();
  555. GUILayout.Label("Kerning Pair already <color=#ffff00>exists!</color>", TMP_UIStyleManager.label);
  556. GUILayout.FlexibleSpace();
  557. GUILayout.EndHorizontal();
  558. if (System.DateTime.Now > timeStamp)
  559. errorCode = 0;
  560. }
  561. }
  562. #endregion
  563. if (serializedObject.ApplyModifiedProperties() || evt_cmd == k_UndoRedo || isAssetDirty)
  564. {
  565. TMPro_EventManager.ON_FONT_PROPERTY_CHANGED(true, m_fontAsset);
  566. isAssetDirty = false;
  567. EditorUtility.SetDirty(target);
  568. }
  569. // Clear selection if mouse event was not consumed.
  570. GUI.enabled = true;
  571. if (currentEvent.type == EventType.MouseDown && currentEvent.button == 0)
  572. m_SelectedAdjustmentRecord = -1;
  573. }
  574. void DisplayPageNavigation(ref int currentPage, int arraySize, int itemsPerPage)
  575. {
  576. Rect pagePos = EditorGUILayout.GetControlRect(false, 20);
  577. pagePos.width /= 3;
  578. int shiftMultiplier = Event.current.shift ? 10 : 1; // Page + Shift goes 10 page forward
  579. // Previous Page
  580. GUI.enabled = currentPage > 0;
  581. if (GUI.Button(pagePos, "Previous Page"))
  582. currentPage -= 1 * shiftMultiplier;
  583. // Page Counter
  584. GUI.enabled = true;
  585. pagePos.x += pagePos.width;
  586. int totalPages = (int)(arraySize / (float)itemsPerPage + 0.999f);
  587. GUI.Label(pagePos, "Page " + (currentPage + 1) + " / " + totalPages, TMP_UIStyleManager.centeredLabel);
  588. // Next Page
  589. pagePos.x += pagePos.width;
  590. GUI.enabled = itemsPerPage * (currentPage + 1) < arraySize;
  591. if (GUI.Button(pagePos, "Next Page"))
  592. currentPage += 1 * shiftMultiplier;
  593. // Clamp page range
  594. currentPage = Mathf.Clamp(currentPage, 0, arraySize / itemsPerPage);
  595. GUI.enabled = true;
  596. }
  597. /// <summary>
  598. ///
  599. /// </summary>
  600. /// <param name="srcGlyphID"></param>
  601. /// <param name="dstGlyphID"></param>
  602. bool AddNewGlyph(int srcIndex, int dstGlyphID)
  603. {
  604. // Make sure Destination Glyph ID doesn't already contain a Glyph
  605. if (m_fontAsset.characterDictionary.ContainsKey(dstGlyphID))
  606. return false;
  607. // Add new element to glyph list.
  608. m_glyphInfoList_prop.arraySize += 1;
  609. // Get a reference to the source glyph.
  610. SerializedProperty sourceGlyph = m_glyphInfoList_prop.GetArrayElementAtIndex(srcIndex);
  611. int dstIndex = m_glyphInfoList_prop.arraySize - 1;
  612. // Get a reference to the target / destination glyph.
  613. SerializedProperty targetGlyph = m_glyphInfoList_prop.GetArrayElementAtIndex(dstIndex);
  614. CopySerializedProperty(sourceGlyph, ref targetGlyph);
  615. // Update the ID of the glyph
  616. targetGlyph.FindPropertyRelative("id").intValue = dstGlyphID;
  617. serializedObject.ApplyModifiedProperties();
  618. m_fontAsset.SortGlyphs();
  619. m_fontAsset.ReadFontDefinition();
  620. return true;
  621. }
  622. /// <summary>
  623. ///
  624. /// </summary>
  625. /// <param name="glyphID"></param>
  626. void RemoveGlyphFromList(int index)
  627. {
  628. if (index > m_glyphInfoList_prop.arraySize)
  629. return;
  630. m_glyphInfoList_prop.DeleteArrayElementAtIndex(index);
  631. serializedObject.ApplyModifiedProperties();
  632. m_fontAsset.ReadFontDefinition();
  633. }
  634. // Check if any of the Style elements were clicked on.
  635. private bool DoSelectionCheck(Rect selectionArea)
  636. {
  637. Event currentEvent = Event.current;
  638. switch (currentEvent.type)
  639. {
  640. case EventType.MouseDown:
  641. if (selectionArea.Contains(currentEvent.mousePosition) && currentEvent.button == 0)
  642. {
  643. currentEvent.Use();
  644. return true;
  645. }
  646. break;
  647. }
  648. return false;
  649. }
  650. GlyphValueRecord GetGlyphAdjustments (SerializedProperty property)
  651. {
  652. GlyphValueRecord record;
  653. record.xPlacement = property.FindPropertyRelative("xPlacement").floatValue;
  654. record.yPlacement = property.FindPropertyRelative("yPlacement").floatValue;
  655. record.xAdvance = property.FindPropertyRelative("xAdvance").floatValue;
  656. record.yAdvance = property.FindPropertyRelative("yAdvance").floatValue;
  657. return record;
  658. }
  659. /// <summary>
  660. ///
  661. /// </summary>
  662. /// <param name="source"></param>
  663. /// <param name="target"></param>
  664. void CopySerializedProperty(SerializedProperty source, ref SerializedProperty target)
  665. {
  666. // TODO : Should make a generic function which copies each of the properties.
  667. target.FindPropertyRelative("id").intValue = source.FindPropertyRelative("id").intValue;
  668. target.FindPropertyRelative("x").floatValue = source.FindPropertyRelative("x").floatValue;
  669. target.FindPropertyRelative("y").floatValue = source.FindPropertyRelative("y").floatValue;
  670. target.FindPropertyRelative("width").floatValue = source.FindPropertyRelative("width").floatValue;
  671. target.FindPropertyRelative("height").floatValue = source.FindPropertyRelative("height").floatValue;
  672. target.FindPropertyRelative("xOffset").floatValue = source.FindPropertyRelative("xOffset").floatValue;
  673. target.FindPropertyRelative("yOffset").floatValue = source.FindPropertyRelative("yOffset").floatValue;
  674. target.FindPropertyRelative("xAdvance").floatValue = source.FindPropertyRelative("xAdvance").floatValue;
  675. target.FindPropertyRelative("scale").floatValue = source.FindPropertyRelative("scale").floatValue;
  676. }
  677. /// <summary>
  678. ///
  679. /// </summary>
  680. /// <param name="searchPattern"></param>
  681. /// <returns></returns>
  682. void SearchGlyphTable (string searchPattern, ref List<int> searchResults)
  683. {
  684. if (searchResults == null) searchResults = new List<int>();
  685. searchResults.Clear();
  686. int arraySize = m_glyphInfoList_prop.arraySize;
  687. for (int i = 0; i < arraySize; i++)
  688. {
  689. SerializedProperty sourceGlyph = m_glyphInfoList_prop.GetArrayElementAtIndex(i);
  690. int id = sourceGlyph.FindPropertyRelative("id").intValue;
  691. // Check for potential match against a character.
  692. if (searchPattern.Length == 1 && id == searchPattern[0])
  693. searchResults.Add(i);
  694. // Check for potential match against decimal id
  695. if (id.ToString().Contains(searchPattern))
  696. searchResults.Add(i);
  697. if (id.ToString("x").Contains(searchPattern))
  698. searchResults.Add(i);
  699. if (id.ToString("X").Contains(searchPattern))
  700. searchResults.Add(i);
  701. }
  702. }
  703. void SearchKerningTable(string searchPattern, ref List<int> searchResults)
  704. {
  705. if (searchResults == null) searchResults = new List<int>();
  706. searchResults.Clear();
  707. int arraySize = m_kerningPairs_prop.arraySize;
  708. for (int i = 0; i < arraySize; i++)
  709. {
  710. SerializedProperty sourceGlyph = m_kerningPairs_prop.GetArrayElementAtIndex(i);
  711. int firstGlyph = sourceGlyph.FindPropertyRelative("m_FirstGlyph").intValue;
  712. int secondGlyph = sourceGlyph.FindPropertyRelative("m_SecondGlyph").intValue;
  713. if (searchPattern.Length == 1)
  714. {
  715. if (firstGlyph == searchPattern[0])
  716. {
  717. searchResults.Add(i);
  718. continue;
  719. }
  720. if (secondGlyph == searchPattern[0])
  721. {
  722. searchResults.Add(i);
  723. continue;
  724. }
  725. }
  726. if (searchPattern.Length == 2)
  727. {
  728. if (firstGlyph == searchPattern[0] && secondGlyph == searchPattern[1])
  729. {
  730. searchResults.Add(i);
  731. continue;
  732. }
  733. }
  734. if (firstGlyph.ToString().Contains(searchPattern))
  735. {
  736. searchResults.Add(i);
  737. continue;
  738. }
  739. //if (firstGlyph.ToString("x").Contains(searchPattern))
  740. //{
  741. // searchResults.Add(i);
  742. // continue;
  743. //}
  744. //if (firstGlyph.ToString("X").Contains(searchPattern))
  745. //{
  746. // searchResults.Add(i);
  747. // continue;
  748. //}
  749. if (secondGlyph.ToString().Contains(searchPattern))
  750. {
  751. searchResults.Add(i);
  752. continue;
  753. }
  754. //if (secondGlyph.ToString("x").Contains(searchPattern))
  755. //{
  756. // searchResults.Add(i);
  757. // continue;
  758. //}
  759. //if (secondGlyph.ToString("X").Contains(searchPattern))
  760. //{
  761. // searchResults.Add(i);
  762. // continue;
  763. //}
  764. }
  765. }
  766. }
  767. }