No Description
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.

TMPro_CreateObjectMenu.cs 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. using UnityEngine;
  2. using UnityEditor;
  3. using UnityEditor.Presets;
  4. using UnityEditor.SceneManagement;
  5. //#if !UNITY_2021_2_OR_NEWER
  6. using UnityEditor.Experimental.SceneManagement;
  7. //#endif
  8. using UnityEngine.UI;
  9. using UnityEngine.EventSystems;
  10. namespace TMPro.EditorUtilities
  11. {
  12. public static class TMPro_CreateObjectMenu
  13. {
  14. /// <summary>
  15. /// Create a TextMeshPro object that works with the Mesh Renderer
  16. /// </summary>
  17. /// <param name="command"></param>
  18. [MenuItem("GameObject/3D Object/Text - TextMeshPro", false, 30)]
  19. static void CreateTextMeshProObjectPerform(MenuCommand command)
  20. {
  21. GameObject go = ObjectFactory.CreateGameObject("Text (TMP)");
  22. // Add support for new prefab mode
  23. StageUtility.PlaceGameObjectInCurrentStage(go);
  24. TextMeshPro textComponent = ObjectFactory.AddComponent<TextMeshPro>(go);
  25. if (textComponent.m_isWaitingOnResourceLoad == false)
  26. {
  27. // Get reference to potential Presets for <TextMeshPro> component
  28. Preset[] presets = Preset.GetDefaultPresetsForObject(textComponent);
  29. if (presets == null || presets.Length == 0)
  30. {
  31. textComponent.text = "Sample text";
  32. textComponent.alignment = TextAlignmentOptions.TopLeft;
  33. }
  34. else
  35. {
  36. textComponent.renderer.sortingLayerID = textComponent._SortingLayerID;
  37. textComponent.renderer.sortingOrder = textComponent._SortingOrder;
  38. }
  39. if (TMP_Settings.autoSizeTextContainer)
  40. {
  41. Vector2 size = textComponent.GetPreferredValues(TMP_Math.FLOAT_MAX, TMP_Math.FLOAT_MAX);
  42. textComponent.rectTransform.sizeDelta = size;
  43. }
  44. else
  45. {
  46. textComponent.rectTransform.sizeDelta = TMP_Settings.defaultTextMeshProTextContainerSize;
  47. }
  48. }
  49. else
  50. {
  51. textComponent.text = "Sample text";
  52. textComponent.alignment = TextAlignmentOptions.TopLeft;
  53. }
  54. Undo.RegisterCreatedObjectUndo(go, "Create " + go.name);
  55. GameObject contextObject = command.context as GameObject;
  56. if (contextObject != null)
  57. {
  58. GameObjectUtility.SetParentAndAlign(go, contextObject);
  59. Undo.SetTransformParent(go.transform, contextObject.transform, "Parent " + go.name);
  60. }
  61. Selection.activeGameObject = go;
  62. }
  63. /// <summary>
  64. /// Create a TextMeshPro object that works with the CanvasRenderer
  65. /// </summary>
  66. /// <param name="command"></param>
  67. [MenuItem("GameObject/UI/Text - TextMeshPro", false, 2001)]
  68. static void CreateTextMeshProGuiObjectPerform(MenuCommand menuCommand)
  69. {
  70. GameObject go = TMP_DefaultControls.CreateText(GetStandardResources());
  71. // Override text color and font size
  72. TextMeshProUGUI textComponent = go.GetComponent<TextMeshProUGUI>();
  73. if (textComponent.m_isWaitingOnResourceLoad == false)
  74. {
  75. // Get reference to potential Presets for <TextMeshProUGUI> component
  76. Preset[] presets = Preset.GetDefaultPresetsForObject(textComponent);
  77. if (presets == null || presets.Length == 0)
  78. {
  79. textComponent.fontSize = TMP_Settings.defaultFontSize;
  80. textComponent.color = Color.white;
  81. textComponent.text = "New Text";
  82. }
  83. if (TMP_Settings.autoSizeTextContainer)
  84. {
  85. Vector2 size = textComponent.GetPreferredValues(TMP_Math.FLOAT_MAX, TMP_Math.FLOAT_MAX);
  86. textComponent.rectTransform.sizeDelta = size;
  87. }
  88. else
  89. {
  90. textComponent.rectTransform.sizeDelta = TMP_Settings.defaultTextMeshProUITextContainerSize;
  91. }
  92. }
  93. else
  94. {
  95. textComponent.fontSize = -99;
  96. textComponent.color = Color.white;
  97. textComponent.text = "New Text";
  98. }
  99. PlaceUIElementRoot(go, menuCommand);
  100. }
  101. [MenuItem("GameObject/UI/Button - TextMeshPro", false, 2031)]
  102. public static void AddButton(MenuCommand menuCommand)
  103. {
  104. GameObject go = TMP_DefaultControls.CreateButton(GetStandardResources());
  105. // Override font size
  106. TMP_Text textComponent = go.GetComponentInChildren<TMP_Text>();
  107. textComponent.fontSize = 24;
  108. PlaceUIElementRoot(go, menuCommand);
  109. }
  110. [MenuItem("GameObject/UI/Input Field - TextMeshPro", false, 2037)]
  111. static void AddTextMeshProInputField(MenuCommand menuCommand)
  112. {
  113. GameObject go = TMP_DefaultControls.CreateInputField(GetStandardResources());
  114. PlaceUIElementRoot(go, menuCommand);
  115. }
  116. [MenuItem("GameObject/UI/Dropdown - TextMeshPro", false, 2036)]
  117. public static void AddDropdown(MenuCommand menuCommand)
  118. {
  119. GameObject go = TMP_DefaultControls.CreateDropdown(GetStandardResources());
  120. PlaceUIElementRoot(go, menuCommand);
  121. }
  122. private const string kUILayerName = "UI";
  123. private const string kStandardSpritePath = "UI/Skin/UISprite.psd";
  124. private const string kBackgroundSpritePath = "UI/Skin/Background.psd";
  125. private const string kInputFieldBackgroundPath = "UI/Skin/InputFieldBackground.psd";
  126. private const string kKnobPath = "UI/Skin/Knob.psd";
  127. private const string kCheckmarkPath = "UI/Skin/Checkmark.psd";
  128. private const string kDropdownArrowPath = "UI/Skin/DropdownArrow.psd";
  129. private const string kMaskPath = "UI/Skin/UIMask.psd";
  130. private static TMP_DefaultControls.Resources s_StandardResources;
  131. private static TMP_DefaultControls.Resources GetStandardResources()
  132. {
  133. if (s_StandardResources.standard == null)
  134. {
  135. s_StandardResources.standard = AssetDatabase.GetBuiltinExtraResource<Sprite>(kStandardSpritePath);
  136. s_StandardResources.background = AssetDatabase.GetBuiltinExtraResource<Sprite>(kBackgroundSpritePath);
  137. s_StandardResources.inputField = AssetDatabase.GetBuiltinExtraResource<Sprite>(kInputFieldBackgroundPath);
  138. s_StandardResources.knob = AssetDatabase.GetBuiltinExtraResource<Sprite>(kKnobPath);
  139. s_StandardResources.checkmark = AssetDatabase.GetBuiltinExtraResource<Sprite>(kCheckmarkPath);
  140. s_StandardResources.dropdown = AssetDatabase.GetBuiltinExtraResource<Sprite>(kDropdownArrowPath);
  141. s_StandardResources.mask = AssetDatabase.GetBuiltinExtraResource<Sprite>(kMaskPath);
  142. }
  143. return s_StandardResources;
  144. }
  145. private static void SetPositionVisibleinSceneView(RectTransform canvasRTransform, RectTransform itemTransform)
  146. {
  147. // Find the best scene view
  148. SceneView sceneView = SceneView.lastActiveSceneView;
  149. if (sceneView == null && SceneView.sceneViews.Count > 0)
  150. sceneView = SceneView.sceneViews[0] as SceneView;
  151. // Couldn't find a SceneView. Don't set position.
  152. if (sceneView == null || sceneView.camera == null)
  153. return;
  154. // Create world space Plane from canvas position.
  155. Camera camera = sceneView.camera;
  156. Vector3 position = Vector3.zero;
  157. Vector2 localPlanePosition;
  158. if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRTransform, new Vector2(camera.pixelWidth / 2, camera.pixelHeight / 2), camera, out localPlanePosition))
  159. {
  160. // Adjust for canvas pivot
  161. localPlanePosition.x = localPlanePosition.x + canvasRTransform.sizeDelta.x * canvasRTransform.pivot.x;
  162. localPlanePosition.y = localPlanePosition.y + canvasRTransform.sizeDelta.y * canvasRTransform.pivot.y;
  163. localPlanePosition.x = Mathf.Clamp(localPlanePosition.x, 0, canvasRTransform.sizeDelta.x);
  164. localPlanePosition.y = Mathf.Clamp(localPlanePosition.y, 0, canvasRTransform.sizeDelta.y);
  165. // Adjust for anchoring
  166. position.x = localPlanePosition.x - canvasRTransform.sizeDelta.x * itemTransform.anchorMin.x;
  167. position.y = localPlanePosition.y - canvasRTransform.sizeDelta.y * itemTransform.anchorMin.y;
  168. Vector3 minLocalPosition;
  169. minLocalPosition.x = canvasRTransform.sizeDelta.x * (0 - canvasRTransform.pivot.x) + itemTransform.sizeDelta.x * itemTransform.pivot.x;
  170. minLocalPosition.y = canvasRTransform.sizeDelta.y * (0 - canvasRTransform.pivot.y) + itemTransform.sizeDelta.y * itemTransform.pivot.y;
  171. Vector3 maxLocalPosition;
  172. maxLocalPosition.x = canvasRTransform.sizeDelta.x * (1 - canvasRTransform.pivot.x) - itemTransform.sizeDelta.x * itemTransform.pivot.x;
  173. maxLocalPosition.y = canvasRTransform.sizeDelta.y * (1 - canvasRTransform.pivot.y) - itemTransform.sizeDelta.y * itemTransform.pivot.y;
  174. position.x = Mathf.Clamp(position.x, minLocalPosition.x, maxLocalPosition.x);
  175. position.y = Mathf.Clamp(position.y, minLocalPosition.y, maxLocalPosition.y);
  176. }
  177. itemTransform.anchoredPosition = position;
  178. itemTransform.localRotation = Quaternion.identity;
  179. itemTransform.localScale = Vector3.one;
  180. }
  181. private static void PlaceUIElementRoot(GameObject element, MenuCommand menuCommand)
  182. {
  183. GameObject parent = menuCommand.context as GameObject;
  184. bool explicitParentChoice = true;
  185. if (parent == null)
  186. {
  187. parent = GetOrCreateCanvasGameObject();
  188. explicitParentChoice = false;
  189. // If in Prefab Mode, Canvas has to be part of Prefab contents,
  190. // otherwise use Prefab root instead.
  191. PrefabStage prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
  192. if (prefabStage != null && !prefabStage.IsPartOfPrefabContents(parent))
  193. parent = prefabStage.prefabContentsRoot;
  194. }
  195. if (parent.GetComponentsInParent<Canvas>(true).Length == 0)
  196. {
  197. // Create canvas under context GameObject,
  198. // and make that be the parent which UI element is added under.
  199. GameObject canvas = CreateNewUI();
  200. Undo.SetTransformParent(canvas.transform, parent.transform, "");
  201. parent = canvas;
  202. }
  203. GameObjectUtility.EnsureUniqueNameForSibling(element);
  204. SetParentAndAlign(element, parent);
  205. if (!explicitParentChoice) // not a context click, so center in sceneview
  206. SetPositionVisibleinSceneView(parent.GetComponent<RectTransform>(), element.GetComponent<RectTransform>());
  207. // This call ensure any change made to created Objects after they where registered will be part of the Undo.
  208. Undo.RegisterFullObjectHierarchyUndo(parent == null ? element : parent, "");
  209. // We have to fix up the undo name since the name of the object was only known after reparenting it.
  210. Undo.SetCurrentGroupName("Create " + element.name);
  211. Selection.activeGameObject = element;
  212. }
  213. private static void SetParentAndAlign(GameObject child, GameObject parent)
  214. {
  215. if (parent == null)
  216. return;
  217. Undo.SetTransformParent(child.transform, parent.transform, "");
  218. RectTransform rectTransform = child.transform as RectTransform;
  219. if (rectTransform)
  220. {
  221. rectTransform.anchoredPosition = Vector2.zero;
  222. Vector3 localPosition = rectTransform.localPosition;
  223. localPosition.z = 0;
  224. rectTransform.localPosition = localPosition;
  225. }
  226. else
  227. {
  228. child.transform.localPosition = Vector3.zero;
  229. }
  230. child.transform.localRotation = Quaternion.identity;
  231. child.transform.localScale = Vector3.one;
  232. SetLayerRecursively(child, parent.layer);
  233. }
  234. private static void SetLayerRecursively(GameObject go, int layer)
  235. {
  236. go.layer = layer;
  237. Transform t = go.transform;
  238. for (int i = 0; i < t.childCount; i++)
  239. SetLayerRecursively(t.GetChild(i).gameObject, layer);
  240. }
  241. public static GameObject CreateNewUI()
  242. {
  243. // Root for the UI
  244. var root = new GameObject("Canvas");
  245. root.layer = LayerMask.NameToLayer(kUILayerName);
  246. Canvas canvas = root.AddComponent<Canvas>();
  247. canvas.renderMode = RenderMode.ScreenSpaceOverlay;
  248. root.AddComponent<CanvasScaler>();
  249. root.AddComponent<GraphicRaycaster>();
  250. // Works for all stages.
  251. StageUtility.PlaceGameObjectInCurrentStage(root);
  252. bool customScene = false;
  253. PrefabStage prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
  254. if (prefabStage != null)
  255. {
  256. root.transform.SetParent(prefabStage.prefabContentsRoot.transform, false);
  257. customScene = true;
  258. }
  259. Undo.RegisterCreatedObjectUndo(root, "Create " + root.name);
  260. // If there is no event system add one...
  261. // No need to place event system in custom scene as these are temporary anyway.
  262. // It can be argued for or against placing it in the user scenes,
  263. // but let's not modify scene user is not currently looking at.
  264. if (!customScene)
  265. CreateEventSystem(false);
  266. return root;
  267. }
  268. private static void CreateEventSystem(bool select)
  269. {
  270. CreateEventSystem(select, null);
  271. }
  272. private static void CreateEventSystem(bool select, GameObject parent)
  273. {
  274. var esys = Object.FindObjectOfType<EventSystem>();
  275. if (esys == null)
  276. {
  277. var eventSystem = new GameObject("EventSystem");
  278. GameObjectUtility.SetParentAndAlign(eventSystem, parent);
  279. esys = eventSystem.AddComponent<EventSystem>();
  280. eventSystem.AddComponent<StandaloneInputModule>();
  281. Undo.RegisterCreatedObjectUndo(eventSystem, "Create " + eventSystem.name);
  282. }
  283. if (select && esys != null)
  284. {
  285. Selection.activeGameObject = esys.gameObject;
  286. }
  287. }
  288. // Helper function that returns a Canvas GameObject; preferably a parent of the selection, or other existing Canvas.
  289. public static GameObject GetOrCreateCanvasGameObject()
  290. {
  291. GameObject selectedGo = Selection.activeGameObject;
  292. // Try to find a gameobject that is the selected GO or one if its parents.
  293. Canvas canvas = (selectedGo != null) ? selectedGo.GetComponentInParent<Canvas>() : null;
  294. if (IsValidCanvas(canvas))
  295. return canvas.gameObject;
  296. // No canvas in selection or its parents? Then use any valid canvas.
  297. // We have to find all loaded Canvases, not just the ones in main scenes.
  298. Canvas[] canvasArray = StageUtility.GetCurrentStageHandle().FindComponentsOfType<Canvas>();
  299. for (int i = 0; i < canvasArray.Length; i++)
  300. if (IsValidCanvas(canvasArray[i]))
  301. return canvasArray[i].gameObject;
  302. // No canvas in the scene at all? Then create a new one.
  303. return CreateNewUI();
  304. }
  305. static bool IsValidCanvas(Canvas canvas)
  306. {
  307. if (canvas == null || !canvas.gameObject.activeInHierarchy)
  308. return false;
  309. // It's important that the non-editable canvas from a prefab scene won't be rejected,
  310. // but canvases not visible in the Hierarchy at all do. Don't check for HideAndDontSave.
  311. if (EditorUtility.IsPersistent(canvas) || (canvas.hideFlags & HideFlags.HideInHierarchy) != 0)
  312. return false;
  313. if (StageUtility.GetStageHandle(canvas.gameObject) != StageUtility.GetCurrentStageHandle())
  314. return false;
  315. return true;
  316. }
  317. }
  318. }