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