From 88b75f90ab4c41c3f11b35ed8ea69a0876065222 Mon Sep 17 00:00:00 2001 From: Finn Sinclair Date: Wed, 7 Dec 2022 17:24:27 -0800 Subject: [PATCH 1/2] CreateElementMenus improvements --- .../Editor/CreateElementMenus.cs | 185 +++++++++++++++--- .../Editor/MRTK.UXComponents.Editor.asmdef | 4 +- 2 files changed, 160 insertions(+), 29 deletions(-) diff --git a/com.microsoft.mrtk.uxcomponents/Editor/CreateElementMenus.cs b/com.microsoft.mrtk.uxcomponents/Editor/CreateElementMenus.cs index d7374560400..7bf18b9cef1 100644 --- a/com.microsoft.mrtk.uxcomponents/Editor/CreateElementMenus.cs +++ b/com.microsoft.mrtk.uxcomponents/Editor/CreateElementMenus.cs @@ -6,6 +6,8 @@ using UnityEngine.UI; using UnityEditor.UI; using UnityEngine.EventSystems; +using UnityEngine.XR.Interaction.Toolkit.UI; +using Microsoft.MixedReality.GraphicsTools; using TMPro; using System.Reflection; using System.Linq; @@ -27,14 +29,15 @@ static internal class CreateElementMenus // The basic building block button; contains an icon, text, and label. private static readonly string ActionButtonPath = AssetDatabase.GUIDToAssetPath("c6b351a67ceb69140b199996bbbea156"); + // CanvasBackplate.mat + // Backplate material for menu plates. + private static readonly string PlateMaterialPath = AssetDatabase.GUIDToAssetPath("65972ebbfd5c529479f9c30fd3ec3f6a"); + // Reflection into internal UGUI editor utilities. private static System.Reflection.MethodInfo PlaceUIElementRoot = null; - private static GameObject CreateElement(string path, MenuCommand menuCommand) + private static GameObject SetupElement(GameObject gameObject, MenuCommand menuCommand) { - Object prefab = AssetDatabase.LoadAssetAtPath(path, typeof(Object)); - GameObject gameObject = PrefabUtility.InstantiatePrefab(prefab) as GameObject; - Undo.RegisterCreatedObjectUndo(gameObject, "Create " + gameObject.name); // This is evil :) // UGUI contains plenty of helper utilities for spawning and managing new Canvas objects @@ -63,34 +66,125 @@ private static GameObject CreateElement(string path, MenuCommand menuCommand) Canvas canvas = gameObject.GetComponentInParent(); RectTransform rt = canvas.GetComponent(); - // If the canvas's only child is us; let's resize the canvas to be a decent starting size. + // If the canvas's only child is us; let's make sure the Canvas has reasonable starting defaults. // Otherwise, it was probably an existing canvas we were added to, so we shouldn't mess with it. if (rt.childCount == 1 && rt.GetChild(0) == gameObject.transform) { - // 1mm : 1 unit measurement ratio. - if (rt.lossyScale != Vector3.one * 0.001f) - { - rt.localScale = Vector3.one * 0.001f; - } + SetReasonableCanvasDefaults(canvas); + + // Reset our own object to zero-position relative to the parent canvas. + gameObject.GetComponent().anchoredPosition3D = Vector3.zero; + } - // 150mm x 150mm. - rt.sizeDelta = Vector2.one * 150.0f; + return gameObject; + } - // All our canvases will be worldspace (by default.) - canvas.renderMode = RenderMode.WorldSpace; + private static GameObject CreateElementFromPath(string path, MenuCommand menuCommand) + { + Object prefab = AssetDatabase.LoadAssetAtPath(path, typeof(Object)); + GameObject gameObject = PrefabUtility.InstantiatePrefab(prefab) as GameObject; + Undo.RegisterCreatedObjectUndo(gameObject, "Create " + gameObject.name); - // 30cm in front of the camera. - rt.position = Camera.main.transform.position + Camera.main.transform.forward * 0.3f; - gameObject.GetComponent().anchoredPosition3D = Vector3.zero; + return SetupElement(gameObject, menuCommand); + } - // PlaceUIElementRoot will have created a GraphicRaycaster for us. - // We don't want that (at least by default) - GraphicRaycaster raycaster = canvas.GetComponent(); - if (raycaster == null) - { - UnityEngine.Object.Destroy(raycaster); - } + private static void SetReasonableCanvasDefaults(Canvas canvas) + { + RectTransform rt = canvas.GetComponent(); + + // 1mm : 1 unit measurement ratio. + if (rt.lossyScale != Vector3.one * 0.001f) + { + rt.localScale = Vector3.one * 0.001f; + } + // 150mm x 150mm. + rt.sizeDelta = Vector2.one * 150.0f; + + // All our canvases will be worldspace (by default.) + canvas.renderMode = RenderMode.WorldSpace; + Undo.RecordObject(canvas, "Set Canvas RenderMode to WorldSpace"); + + // 30cm in front of the camera. + rt.position = Camera.main.transform.position + Camera.main.transform.forward * 0.3f; + Undo.RecordObject(rt, "Set Canvas Position"); + + // No GraphicRaycaster by default. Users can add one, if they like. + GraphicRaycaster raycaster = canvas.GetComponent(); + if (raycaster != null) + { + Undo.DestroyObjectImmediate(raycaster); + } + + // CanvasScaler should be there by default. + CanvasScaler scaler = canvas.GetComponent(); + if (scaler == null) + { + scaler = Undo.AddComponent(canvas.gameObject); } + } + + [MenuItem("GameObject/UI/MRTK/Canvas", false, 0)] + private static void CreateEmptyCanvas(MenuCommand menuCommand) + { + Undo.SetCurrentGroupName("Create Canvas"); + int group = Undo.GetCurrentGroup(); + + GameObject gameObject = new GameObject("Canvas"); + Undo.RegisterCreatedObjectUndo(gameObject, "Create blank MRTK Canvas"); + + Canvas canvas = Undo.AddComponent(gameObject); + SetReasonableCanvasDefaults(canvas); + + Undo.CollapseUndoOperations(group); + } + + [MenuItem("GameObject/UI/MRTK/Canvas + Graphic Raycasting", false, 0)] + private static void CreateGraphicRaycastingCanvas(MenuCommand menuCommand) + { + Undo.SetCurrentGroupName("Create Canvas (Raycasting-enabled)"); + int group = Undo.GetCurrentGroup(); + + GameObject gameObject = new GameObject("Canvas"); + Undo.RegisterCreatedObjectUndo(gameObject, "Create MRTK Canvas with Graphic Raycasting"); + + Canvas canvas = Undo.AddComponent(gameObject); + SetReasonableCanvasDefaults(canvas); + + Undo.AddComponent(gameObject); + Undo.AddComponent(gameObject); + + Undo.CollapseUndoOperations(group); + } + + // TODO: This may end up being prefabified at some point. Also TODO, + // ensure this gets theming scripts when that system is ready. + [MenuItem("GameObject/UI/MRTK/Plate", false, 0)] + private static GameObject CreatePlate(MenuCommand menuCommand) + { + Undo.SetCurrentGroupName("Create Plate"); + int group = Undo.GetCurrentGroup(); + + GameObject gameObject = new GameObject("Plate", typeof(CanvasElementRoundedRect)); + + + Undo.RegisterCreatedObjectUndo(gameObject, "Create " + gameObject.name); + + // gameObject.transform.SetParent((menuCommand.context as GameObject).transform, false); + + SetupElement(gameObject, menuCommand); + + CanvasElementRoundedRect roundedRect = gameObject.GetComponent(); + + roundedRect.raycastTarget = false; + roundedRect.material = AssetDatabase.LoadAssetAtPath(PlateMaterialPath, typeof(Material)) as Material; + roundedRect.Radius = 13.0f; + roundedRect.Thickness = 2.0f; + roundedRect.Wedges = 8; + roundedRect.SmoothEdges = true; + Undo.RecordObject(roundedRect, "Set Plate RoundedRect properties"); + + + Undo.CollapseUndoOperations(group); return gameObject; } @@ -98,31 +192,66 @@ private static GameObject CreateElement(string path, MenuCommand menuCommand) [MenuItem("GameObject/UI/MRTK/Action Button", false, 0)] private static void CreateActionButton(MenuCommand menuCommand) { - CreateElement(ActionButtonPath, menuCommand); + CreateElementFromPath(ActionButtonPath, menuCommand); } [MenuItem("GameObject/UI/MRTK/Action Button (Wide)", false, 1)] - private static void CreateActionButtonWide(MenuCommand menuCommand) + private static GameObject CreateActionButtonWide(MenuCommand menuCommand) { - GameObject gameObject = CreateElement(ActionButtonPath, menuCommand); + Undo.SetCurrentGroupName("Create Action Button (wide)"); + int group = Undo.GetCurrentGroup(); + + GameObject gameObject = CreateElementFromPath(ActionButtonPath, menuCommand); RectTransform rt = gameObject.GetComponent(); rt.sizeDelta = new Vector2(128.0f, 32.0f); + Undo.RecordObject(rt, "Set Action Button (Wide) size"); LayoutElement le = gameObject.GetComponent(); le.minWidth = 128.0f; + Undo.RecordObject(le, "Set Action Button (Wide) min width"); var text = gameObject.GetComponentsInChildren(true).Where(t => t.name == "Text").First(); text.gameObject.SetActive(true); text.alignment = TextAlignmentOptions.Left; text.text = "Header\nMeta text goes here"; + Undo.RecordObject(text, "Set Action Button (Wide) text"); PrefabUtility.RecordPrefabInstancePropertyModifications(gameObject); + + return gameObject; } [MenuItem("GameObject/UI/MRTK/Empty Button", false, 2)] private static void CreateEmptyButton(MenuCommand menuCommand) { - CreateElement(EmptyButtonPath, menuCommand); + CreateElementFromPath(EmptyButtonPath, menuCommand); + } + + [MenuItem("GameObject/UI/MRTK/List Menu", false, 0)] + private static void CreateListMenu(MenuCommand menuCommand) + { + Undo.SetCurrentGroupName("Create ListMenu"); + int group = Undo.GetCurrentGroup(); + + var plate = CreatePlate(menuCommand); + + var layout = Undo.AddComponent(plate); + layout.padding = new RectOffset(4, 4, 4, 4); + Undo.RecordObject(layout, "Set ListMenu VerticalLayoutGroup properties"); + + var fitter = Undo.AddComponent(plate); + fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize; + fitter.horizontalFit = ContentSizeFitter.FitMode.PreferredSize; + Undo.RecordObject(fitter, "Set ListMenu ContentSizeFitter properties"); + + for (int i = 0; i < 4; i++) + { + var button = CreateActionButtonWide(menuCommand); + Undo.SetTransformParent(button.transform, plate.transform, "Reparent button to plate"); + + } + + Undo.CollapseUndoOperations(group); } } } \ No newline at end of file diff --git a/com.microsoft.mrtk.uxcomponents/Editor/MRTK.UXComponents.Editor.asmdef b/com.microsoft.mrtk.uxcomponents/Editor/MRTK.UXComponents.Editor.asmdef index becf3dec6d6..a426c93d534 100644 --- a/com.microsoft.mrtk.uxcomponents/Editor/MRTK.UXComponents.Editor.asmdef +++ b/com.microsoft.mrtk.uxcomponents/Editor/MRTK.UXComponents.Editor.asmdef @@ -2,7 +2,9 @@ "name": "Microsoft.MixedReality.Toolkit.UXComponents.Editor", "rootNamespace": "Microsoft.MixedReality.Toolkit.Editor", "references": [ - "Unity.TextMeshPro" + "Unity.TextMeshPro", + "Unity.XR.Interaction.Toolkit", + "Microsoft.MixedReality.GraphicsTools" ], "includePlatforms": [ "Editor" From 03ded527c498ae65c3b2b617143cf4b76e768c0a Mon Sep 17 00:00:00 2001 From: Finn Sinclair Date: Thu, 8 Dec 2022 14:28:23 -0800 Subject: [PATCH 2/2] Adding transitive deps explicitly to uxcomponents package.json --- com.microsoft.mrtk.uxcomponents/package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/com.microsoft.mrtk.uxcomponents/package.json b/com.microsoft.mrtk.uxcomponents/package.json index de756cd85dc..ffc3292d7df 100644 --- a/com.microsoft.mrtk.uxcomponents/package.json +++ b/com.microsoft.mrtk.uxcomponents/package.json @@ -15,9 +15,11 @@ }, "unity": "2020.3", "dependencies": { + "com.microsoft.mrtk.graphicstools.unity": "0.4.0", "com.microsoft.mrtk.uxcore": "3.0.0-development", "com.microsoft.mrtk.spatialmanipulation": "3.0.0-development", - "com.microsoft.mrtk.standardassets": "3.0.0-development" + "com.microsoft.mrtk.standardassets": "3.0.0-development", + "com.unity.xr.interaction.toolkit": "2.2.0" }, "msftTestDependencies": { "com.microsoft.mrtk.input": "3.0.0-development"