diff --git a/Packages/nadena.dev.modular-avatar/Editor/MenuTree.cs b/Packages/nadena.dev.modular-avatar/Editor/MenuTree.cs index 1f77d4b3..46029b9b 100644 --- a/Packages/nadena.dev.modular-avatar/Editor/MenuTree.cs +++ b/Packages/nadena.dev.modular-avatar/Editor/MenuTree.cs @@ -12,19 +12,20 @@ namespace nadena.dev.modular_avatar.core.editor { public struct ChildElement { public string menuName; public VRCExpressionsMenu menu; + public VRCExpressionsMenu parent; public ModularAvatarMenuInstaller installer; + public bool isInstallerRoot; } - - private readonly VRCExpressionsMenu _rootMenu; - private readonly HashSet _included; - private readonly Dictionary> _childrenMap; - private readonly Dictionary> _flashedChildrenMap; + private readonly HashSet _included; + + private readonly VRCExpressionsMenu _rootMenu; + private readonly Dictionary> _menuChildrenMap; public MenuTree(VRCAvatarDescriptor descriptor) { this._rootMenu = descriptor.expressionsMenu; this._included = new HashSet(); - this._childrenMap = new Dictionary>(); + this._menuChildrenMap = new Dictionary>(); if (this._rootMenu == null) { this._rootMenu = ScriptableObject.CreateInstance(); @@ -41,45 +42,97 @@ namespace nadena.dev.modular_avatar.core.editor { public void MappingMenuInstaller(ModularAvatarMenuInstaller installer) { if (!installer.enabled) return; if (installer.menuToAppend == null) return; - this.MappingMenu(installer.menuToAppend, installer); + this.MappingMenu(installer); } public IEnumerable GetChildren(VRCExpressionsMenu parent) { // TODO: ライブラリとするのであれば、複製したリスト or ImmutableArray,を返すのが好ましい if (parent == null) parent = this._rootMenu; - return this._childrenMap.TryGetValue(parent, out List children) + return this._menuChildrenMap.TryGetValue(parent, out List children) ? children : Enumerable.Empty(); } - private void MappingMenu(VRCExpressionsMenu root, ModularAvatarMenuInstaller installer = null) { + public IEnumerable GetChildInstallers(ModularAvatarMenuInstaller parentInstaller) { + HashSet visitedMenus = new HashSet(); Queue queue = new Queue(); - queue.Enqueue(root); - bool first = true; - - while (queue.Count > 0) { - VRCExpressionsMenu parent = queue.Dequeue(); - IEnumerable> childMenus = GetChildMenus(parent); - - if (first && installer != null) { - parent = installer.installTargetMenu != null ? installer.installTargetMenu : _rootMenu; - + if (parentInstaller != null && parentInstaller.menuToAppend == null) yield break; + if (parentInstaller == null) { + queue.Enqueue(this._rootMenu); + } else { + if (parentInstaller.menuToAppend == null) yield break; + foreach (KeyValuePair childMenu in GetChildMenus(parentInstaller.menuToAppend)) { + queue.Enqueue(childMenu.Value); } - - foreach (KeyValuePair childMenu in childMenus) { - if (!this._childrenMap.TryGetValue(parent, out List children)) { - children = new List(); - this._childrenMap[parent] = children; + } + while (queue.Count > 0) { + VRCExpressionsMenu parentMenu = queue.Dequeue(); + if (visitedMenus.Contains(parentMenu)) continue; + visitedMenus.Add(parentMenu); + HashSet returnedInstallers = new HashSet(); + foreach (ChildElement childElement in this.GetChildren(parentMenu)) { + if (!childElement.isInstallerRoot) { + queue.Enqueue(childElement.menu); + continue; } - ChildElement childElement = new ChildElement { menuName = childMenu.Key, menu = childMenu.Value, installer = installer }; - children.Add(childElement); - if (this._included.Contains(childElement.menu)) continue; - queue.Enqueue(childElement.menu); - this._included.Add(childElement.menu); + if (returnedInstallers.Contains(childElement.installer)) continue; + returnedInstallers.Add(childElement.installer); + yield return childElement; } + } + } - first = false; + + private void MappingMenu(VRCExpressionsMenu root) { + foreach (KeyValuePair childMenu in GetChildMenus(root)) { + this.MappingMenu(root, new ChildElement { + menuName = childMenu.Key, + menu = childMenu.Value + }); + } + } + + private void MappingMenu(ModularAvatarMenuInstaller installer) { + IEnumerable> childMenus = GetChildMenus(installer.menuToAppend); + IEnumerable parents = Enumerable.Empty(); + if (installer.installTargetMenu != null && + ClonedMenuMappings.TryGetClonedMenus(installer.installTargetMenu, out ImmutableArray parentMenus)) { + parents = parentMenus; + } + + VRCExpressionsMenu[] parentsMenus = parents.DefaultIfEmpty(installer.installTargetMenu).ToArray(); + + foreach (KeyValuePair childMenu in childMenus) { + ChildElement childElement = new ChildElement { + menuName = childMenu.Key, + menu = childMenu.Value, + installer = installer, + isInstallerRoot = true + }; + foreach (VRCExpressionsMenu parentMenu in parentsMenus) { + this.MappingMenu(parentMenu, childElement); + } + } + } + + private void MappingMenu(VRCExpressionsMenu parent, ChildElement childElement) { + if (parent == null) parent = this._rootMenu; + childElement.parent = parent; + if (!this._menuChildrenMap.TryGetValue(parent, out List children)) { + children = new List(); + this._menuChildrenMap[parent] = children; + } + + children.Add(childElement); + if (this._included.Contains(childElement.menu)) return; + this._included.Add(childElement.menu); + foreach (KeyValuePair childMenu in GetChildMenus(childElement.menu)) { + this.MappingMenu(childElement.menu, new ChildElement { + menuName = childMenu.Key, + menu = childMenu.Value, + installer = childElement.installer + }); } }