using System.Collections.Generic; using System.Linq; using NUnit.Framework; using UnityEngine; using VRC.SDK3.Avatars.Components; using VRC.SDK3.Avatars.ScriptableObjects; using static VRC.SDK3.Avatars.ScriptableObjects.VRCExpressionsMenu; using static VRC.SDK3.Avatars.ScriptableObjects.VRCExpressionsMenu.Control; // ReSharper disable once CheckNamespace namespace nadena.dev.modular_avatar.core.editor { public class MenuTree { private VRCAvatarDescriptor _descriptor; private readonly VRCExpressionsMenu _rootMenu; private readonly HashSet _included; private readonly Dictionary> _childrenMap; public MenuTree(VRCAvatarDescriptor descriptor) { this._descriptor = descriptor; this._rootMenu = descriptor.expressionsMenu; this._included = new HashSet(); this._childrenMap = new Dictionary>(); if (this._rootMenu == null) { this._rootMenu = ScriptableObject.CreateInstance(); } this._included.Add(this._rootMenu); } public void AvatarsMenuMapping() { if (this._rootMenu == null) return; this.MappingMenu(this._rootMenu); } public void AddMenuInstaller(ModularAvatarMenuInstaller installer) { if (installer.menuToAppend == null) return; VRCExpressionsMenu parent = installer.installTargetMenu; if (parent == null) parent = this._rootMenu; this.MappingMenu(installer.menuToAppend, parent); } private void MappingMenu(VRCExpressionsMenu root, VRCExpressionsMenu customParent = null) { Queue queue = new Queue(); queue.Enqueue(root); while (queue.Count > 0) { VRCExpressionsMenu parent = queue.Dequeue(); IEnumerable subMenus = GetSubMenus(parent); if (customParent != null) { parent = customParent; customParent = null; } foreach (VRCExpressionsMenu subMenu in subMenus) { if (!this._childrenMap.TryGetValue(parent, out List children)) { children = new List(); this._childrenMap[parent] = children; } children.Add(subMenu); if (this._included.Contains(subMenu)) continue; queue.Enqueue(subMenu); this._included.Add(subMenu); } } } private static IEnumerable GetSubMenus(VRCExpressionsMenu expressionsMenu) { return expressionsMenu.controls .Where(control => control.type == ControlType.SubMenu && control.subMenu != null) .Select(control => control.subMenu); } } }