/* * 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; using UnityEngine; using TMPro; using System.Collections.Generic; using System.Threading.Tasks; namespace Oculus.Interaction.DebugTree { /// /// Interface indicating the inheriting type can be represented as a node in a tree-like UI structure for debugging /// and other development purposes. /// /// /// This type is explicitly intended for use alongside ; for a canonical usage example, /// see . /// /// public interface INodeUI where TLeaf : class { /// /// The region in which children of this node within the UI tree should be displayed. /// RectTransform ChildArea { get; } /// /// This method should only be called by the which contains this node. Associates the /// provided with this UI representation for it. /// /// The node which this INodeUI should be bound to represent /// Indicates whether or not is the root node of the tree /// Indicates whether has already been bound to a different INodeUI void Bind(ITreeNode node, bool isRoot, bool isDuplicate); } public abstract class DebugTreeUI : MonoBehaviour where TLeaf : class { [Tooltip("Node prefabs will be instantiated inside of this content area.")] [SerializeField] private RectTransform _contentArea; [Tooltip("This title text will display the GameObject name of the IActiveState.")] [SerializeField, Optional] private TMP_Text _title; [Tooltip("If true, the tree UI will be built on Start.")] [SerializeField] private bool _buildTreeOnStart; protected abstract TLeaf Value { get; } protected abstract INodeUI NodePrefab { get; } private DebugTree _tree; private Dictionary, INodeUI> _nodeToUI = new Dictionary, INodeUI>(); protected virtual void Start() { this.AssertField(Value, nameof(Value)); this.AssertField(NodePrefab, nameof(NodePrefab)); this.AssertField(_contentArea, nameof(_contentArea)); if (_buildTreeOnStart) { StartCoroutine(BuildTree()); } } public IEnumerator BuildTree() { _tree = CreateTree(Value); Task task = _tree.RebuildAsync(); yield return new WaitUntil(() => task.IsCompleted); _nodeToUI.Clear(); ClearContentArea(); SetTitleText(); BuildTreeRecursive(_contentArea, _tree.GetRootNode(), true); } private void BuildTreeRecursive( RectTransform parent, ITreeNode node, bool isRoot) { INodeUI nodeUI = Instantiate(NodePrefab as Object, parent) as INodeUI; bool isDuplicate = _nodeToUI.ContainsKey(node); nodeUI.Bind(node, isRoot, isDuplicate); if (!isDuplicate) { _nodeToUI.Add(node, nodeUI); foreach (var child in node.Children) { BuildTreeRecursive(nodeUI.ChildArea, child, false); } } } private void ClearContentArea() { for (int i = 0; i < _contentArea.childCount; ++i) { Transform child = _contentArea.GetChild(i); if (child != null && child.TryGetComponent>(out _)) { Destroy(child.gameObject); } } } private void SetTitleText() { if (_title != null) { _title.text = TitleForValue(Value); } } protected abstract DebugTree CreateTree(TLeaf value); protected abstract string TitleForValue(TLeaf value); #if UNITY_EDITOR private void OnValidate() { SetTitleText(); } #endif } }