/* * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * Licensed under the Oculus SDK License Agreement (the "License"); * you may not use the Oculus SDK except in compliance with the License, * which is provided at the time of installation or download, or which * otherwise accompanies this software in either electronic or hard copy form. * * You may obtain a copy of the License at * * https://developer.oculus.com/licenses/oculussdk/ * * Unless required by applicable law or agreed to in writing, the Oculus SDK * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System.Collections.Generic; using UnityEditor; using UnityEngine; namespace Meta.XR.BuildingBlocks.Editor { [CustomEditor(typeof(BuildingBlock))] public class BuildingBlockEditor : UnityEditor.Editor { private class Styles { internal const float ThumbnailRatio = 1.8f; internal const int Border = 1; internal const float SmallIconSize = 16.0f; internal const float ItemHeight = 48.0f; internal const int Padding = 4; internal const int TightPadding = 2; internal readonly GUIStyle GridItemStyle = new GUIStyle() { margin = new RectOffset(0, 0, 0, 0), padding = new RectOffset(Border, Border, Border, Border), stretchWidth = false, stretchHeight = false, normal = { background = OVREditorUtils.MakeTexture(1, 1, OVREditorUtils.HexToColor("#1d1d1d")) } }; internal readonly GUIStyle DescriptionAreaStyle = new GUIStyle() { stretchHeight = false, padding = new RectOffset(Padding, Padding, Padding, Padding), margin = new RectOffset(0, 0, 0, Border), fixedHeight = ItemHeight, normal = { background = OVREditorUtils.MakeTexture(1, 1, OVREditorUtils.HexToColor("#3e3e3e")) } }; internal readonly GUIStyle IconStyle = new GUIStyle(EditorStyles.label) { margin = new RectOffset(0, 0, 0, 0), padding = new RectOffset(0, 0, 0, 0), fixedWidth = SmallIconSize, stretchWidth = false }; internal readonly GUIStyle LargeButton = new GUIStyle(EditorStyles.miniButton) { clipping = TextClipping.Overflow, fixedHeight = ItemHeight - Padding * 2, fixedWidth = ItemHeight - Padding * 2, margin = new RectOffset(2, 2, 2, 2), padding = new RectOffset(Padding, Padding, Padding, Padding) }; internal readonly GUIStyle LabelStyle = new GUIStyle(EditorStyles.boldLabel); internal readonly GUIStyle LabelHoverStyle = new GUIStyle(EditorStyles.boldLabel) { normal = { textColor = Color.white } }; internal readonly GUIStyle SubtitleStyle = new GUIStyle(EditorStyles.label) { fontStyle = FontStyle.Italic, normal = { textColor = Color.gray } }; internal readonly GUIStyle InfoStyle = new GUIStyle(EditorStyles.label) { fontSize = 10, normal = { textColor = Color.gray } }; internal readonly GUIStyle ExperimentalAreaStyle = new GUIStyle() { margin = new RectOffset(Padding, Padding, Padding, Padding), padding = new RectOffset(Padding, TightPadding, TightPadding, Padding), fixedHeight = 18, normal = { background = OVREditorUtils.MakeTexture(1, 1, OVREditorUtils.HexToColor("#3e3e3eAA")) } }; internal readonly GUIStyle ExperimentalTextStyle = new GUIStyle(EditorStyles.boldLabel) { fontSize = 10, normal = { textColor = ExperimentalColor }, wordWrap = true }; internal static readonly OVRGUIContent ErrorIcon = OVREditorUtils.CreateContent("ovr_error_greybg.png", OVRGUIContent.Source.BuildingBlocksIcons); internal static readonly OVRGUIContent SuccessIcon = OVREditorUtils.CreateContent("ovr_success_greybg.png", OVRGUIContent.Source.BuildingBlocksIcons); internal static readonly OVRGUIContent ExperimentalIcon = OVREditorUtils.CreateContent("ovr_icon_experimental.png", OVRGUIContent.Source.BuildingBlocksIcons, "Experimental"); internal static readonly Color AccentColor = OVREditorUtils.HexToColor("#a29de5"); internal static readonly Color ExperimentalColor = OVREditorUtils.HexToColor("#eba333"); } private static Styles _styles; private static Styles styles => _styles ??= new Styles(); private BuildingBlock _block; private BlockData _blockData; public override void OnInspectorGUI() { _block = target as BuildingBlock; _blockData = _block.GetBlockData(); if (_blockData == null) { return; } var currentWidth = EditorGUIUtility.currentViewWidth; var expectedHeight = currentWidth / Styles.ThumbnailRatio; expectedHeight *= 0.5f; // Thumbnail var rect = GUILayoutUtility.GetRect(currentWidth, expectedHeight); rect.x -= 20; rect.width += 40; rect.y -= 4; GUI.DrawTexture(rect, _blockData.Thumbnail, ScaleMode.ScaleAndCrop); // Experimental tag if (_blockData.Experimental) { GUILayout.BeginArea(new Rect(styles.ExperimentalAreaStyle.margin.left, styles.ExperimentalAreaStyle.margin.top, currentWidth, expectedHeight)); const string experimentalStr = "Experimental"; var width = styles.ExperimentalTextStyle.CalcSize(new GUIContent(experimentalStr)).x; var height = styles.ExperimentalAreaStyle.fixedHeight - Styles.TightPadding * 2.0f; GUILayout.BeginHorizontal(styles.ExperimentalAreaStyle, GUILayout.Width(height + width), GUILayout.Height(height)); EditorGUILayout.LabelField(Styles.ExperimentalIcon, styles.ExperimentalTextStyle, GUILayout.Width(height), GUILayout.Height(height)); EditorGUILayout.LabelField(experimentalStr, styles.ExperimentalTextStyle, GUILayout.Width(width), GUILayout.Height(height)); EditorGUILayout.EndHorizontal(); GUILayout.EndArea(); } // Separator rect = GUILayoutUtility.GetRect(currentWidth, 1); rect.x -= 20; rect.width += 40; rect.y -= 4; GUI.DrawTexture(rect, OVREditorUtils.MakeTexture(1, 1, Styles.AccentColor), ScaleMode.ScaleAndCrop); ShowBlock(_blockData, _block, false, false, true); ShowBlockDataList("Dependencies", _blockData.GetAllDependencyDatas()); ShowBlockList("Used by", _blockData.GetUsingBlocksInScene()); } private bool ShowLargeButton(GUIContent icon) { var previousColor = GUI.color; GUI.color = Color.white; var hit = GUILayout.Button(icon, styles.LargeButton); GUI.color = previousColor; EditorGUIUtility.AddCursorRect(GUILayoutUtility.GetLastRect(), MouseCursor.Link); return hit; } private void ShowBlockDataList(string name, List list) { EditorGUILayout.LabelField(name, EditorStyles.boldLabel); if (list.Count == 0) { EditorGUILayout.LabelField("No dependency blocks are required.", EditorStyles.helpBox); } else { foreach (var dependency in list) { ShowBlock(dependency, null, true, true, false); } } } private void ShowBlockList(string name, List list) { EditorGUILayout.LabelField(name, EditorStyles.boldLabel); if (list.Count == 0) { EditorGUILayout.LabelField("No dependency blocks are required.", EditorStyles.helpBox); } else { foreach (var dependency in list) { ShowBlock(null, dependency, true, true, false); } } } private void ShowBlock(BlockData data, BuildingBlock block, bool asGridItem, bool showAction, bool showBb) { var previousIndent = EditorGUI.indentLevel; EditorGUI.indentLevel = 0; data = data ? data : block.GetBlockData(); block = block ? block : data.GetBlock(); // Thumbnail if (asGridItem) { EditorGUILayout.BeginHorizontal(styles.GridItemStyle); EditorGUILayout.BeginHorizontal(styles.DescriptionAreaStyle); var expectedSize = Styles.ItemHeight; var rect = GUILayoutUtility.GetRect(0, expectedSize); rect.y -= Styles.Padding; rect.x -= Styles.Padding; rect.width = Styles.ItemHeight; GUI.DrawTexture(rect, data.Thumbnail, ScaleMode.ScaleAndCrop); EditorGUILayout.Space(Styles.ItemHeight - Styles.Padding - Styles.SmallIconSize * 0.5f - 2); EditorGUILayout.LabelField(block != null ? Styles.SuccessIcon : Styles.ErrorIcon, styles.IconStyle, GUILayout.Width(Styles.SmallIconSize), GUILayout.Height(Styles.ItemHeight - Styles.Padding * 2)); } else { EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginHorizontal(); } // Label EditorGUILayout.BeginVertical(); EditorGUILayout.BeginHorizontal(); var labelStyle = styles.LabelStyle; var labelContent = new GUIContent(data.BlockName); EditorGUILayout.LabelField(labelContent, labelStyle, GUILayout.Width(labelStyle.CalcSize(labelContent).x)); labelStyle = styles.SubtitleStyle; labelContent = new GUIContent(data.Sdk.ToString()); EditorGUILayout.LabelField(labelContent, styles.SubtitleStyle, GUILayout.Width(labelStyle.CalcSize(labelContent).x)); EditorGUILayout.EndHorizontal(); labelContent = new GUIContent(block ? block.name : "Not Installed"); EditorGUILayout.LabelField(labelContent, styles.InfoStyle); EditorGUILayout.EndVertical(); GUILayout.FlexibleSpace(); if (showAction) { if (block != null) { if (ShowLargeButton(Utils.GotoIcon)) { block.SelectBlockInScene(); } } else { if (ShowLargeButton(Utils.AddIcon)) { data.AddToProject(); } } } if (showBb && ShowLargeButton(Utils.StatusIcon)) { BuildingBlocksWindow.ShowWindow("BuildingBlockEditor"); } EditorGUILayout.EndHorizontal(); EditorGUILayout.EndHorizontal(); EditorGUI.indentLevel = previousIndent; } } }