Files
2025-07-04 20:33:06 +03:00

318 lines
12 KiB
C#

/*
* 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<BlockData> 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<BuildingBlock> 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;
}
}
}