diff --git a/Packages/nadena.dev.modular-avatar/Editor/MenuTree.cs b/Packages/nadena.dev.modular-avatar/Editor/MenuTree.cs new file mode 100644 index 00000000..b775dec6 --- /dev/null +++ b/Packages/nadena.dev.modular-avatar/Editor/MenuTree.cs @@ -0,0 +1,74 @@ +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); + } + } +} \ No newline at end of file diff --git a/Packages/nadena.dev.modular-avatar/Editor/MenuTree.cs.meta b/Packages/nadena.dev.modular-avatar/Editor/MenuTree.cs.meta new file mode 100644 index 00000000..dc5993a1 --- /dev/null +++ b/Packages/nadena.dev.modular-avatar/Editor/MenuTree.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: effd4557902f4578af42d3bdfb7f876d +timeCreated: 1670746991 \ No newline at end of file