using System.Collections.Generic; using VRC.SDK3.Avatars.ScriptableObjects; namespace nadena.dev.modular_avatar.core { /// /// The menu install target includes the controls of the target menu installer at the point of reference. /// Notably, this can include multiple controls. /// /// One tricky aspect of this feature is that we need to disambiguate when a menu installer also cites a target menu. /// Generally, if an installer is targeted by any menu install target (even if - especially if - disabled), we /// ignore its install target configuration entirely. /// /// We can also end up with a loop between install targets; in this case, we break the loop at an arbitrary point. /// internal class ModularAvatarMenuInstallTarget : MenuSource { public ModularAvatarMenuInstaller installer; private static HashSet _recursing = new HashSet(); internal delegate T Returning(); /** * Temporarily clears the list of install targets we're recursing through. This is useful if we need to generate * a submenu; these have their own recursion stack, and we shouldn't truncate the set of controls registered on * a different submenu that happens to transclude the same point. */ internal static T PushRecursing(Returning callback) { HashSet oldRecursing = _recursing; _recursing = new HashSet(); try { return callback(); } finally { _recursing = oldRecursing; } } internal override VRCExpressionsMenu.Control[] GenerateMenu() { if (installer == null) return new VRCExpressionsMenu.Control[] { }; _recursing.Add(this); try { var source = installer.GetComponent(); if (source != null) { return source.GenerateMenu(); } else { // ReSharper disable once Unity.NoNullPropagation return installer.menuToAppend?.controls?.ToArray() ?? new VRCExpressionsMenu.Control[] { }; } } finally { _recursing.Remove(this); } } } }