Skip to content

Commit

Permalink
feat: add API to open Menu Installer's selection UI
Browse files Browse the repository at this point in the history
Closes: #1327
  • Loading branch information
bdunderscore committed Nov 17, 2024
1 parent ef0beec commit 14e814d
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 68 deletions.
164 changes: 96 additions & 68 deletions Editor/Inspector/Menu/MenuInstallerEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,29 @@ internal class MenuInstallerEditor : MAEditorBase

private Dictionary<VRCExpressionsMenu, List<ModularAvatarMenuInstaller>> _menuInstallersMap;

private static Editor _cachedEditor;

[InitializeOnLoadMethod]
private static void Init()
{
ModularAvatarMenuInstaller._openSelectMenu = OpenSelectInstallTargetMenu;
}

private static void OpenSelectInstallTargetMenu(ModularAvatarMenuInstaller installer)
{
CreateCachedEditor(installer, typeof(MenuInstallerEditor), ref _cachedEditor);

var editor = (MenuInstallerEditor)_cachedEditor;
editor.OnEnable();

var serializedObject = editor.serializedObject;
var installTo = serializedObject.FindProperty(nameof(ModularAvatarMenuInstaller.installTargetMenu));

var root = editor.FindCommonAvatar();

editor.OpenSelectMenu(root, installTo);
}

private void OnEnable()
{
_installer = (ModularAvatarMenuInstaller) target;
Expand Down Expand Up @@ -215,74 +238,7 @@ protected override void OnInnerInspectorGUI()
var avatar = commonAvatar;
if (avatar != null && InstallTargets.Count == 1 && GUILayout.Button(G("menuinstall.selectmenu")))
{
AvMenuTreeViewWindow.Show(avatar, _installer, menu =>
{
if (InstallTargets.Count != 1 || menu == InstallTargets[0]) return;

if (InstallTargets[0] is ModularAvatarMenuInstallTarget oldTarget && oldTarget != null)
{
DestroyInstallTargets();
}

if (menu is ValueTuple<object, object> vt) // TODO: This should be a named type...
{
// Menu, ContextCallback
menu = vt.Item1;
}

if (menu is ModularAvatarMenuItem item)
{
if (item.MenuSource == SubmenuSource.MenuAsset)
{
menu = item.Control.subMenu;
}
else
{
var menuParent = item.menuSource_otherObjectChildren != null
? item.menuSource_otherObjectChildren
: item.gameObject;

menu = new MenuNodesUnder(menuParent);
}
}
else if (menu is ModularAvatarMenuGroup group)
{
if (group.targetObject != null) menu = new MenuNodesUnder(group.targetObject);
else menu = new MenuNodesUnder(group.gameObject);
}

if (menu is VRCExpressionsMenu expMenu)
{
if (expMenu == avatar.expressionsMenu) installTo.objectReferenceValue = null;
else installTo.objectReferenceValue = expMenu;
}
else if (menu is RootMenu)
{
installTo.objectReferenceValue = null;
}
else if (menu is MenuNodesUnder nodesUnder)
{
installTo.objectReferenceValue = null;

foreach (var target in targets.Cast<Component>().OrderBy(ObjectHierarchyOrder))
{
var installer = (ModularAvatarMenuInstaller) target;
var child = new GameObject();
Undo.RegisterCreatedObjectUndo(child, "Set install target");
child.transform.SetParent(nodesUnder.root.transform, false);
child.name = installer.gameObject.name;

var targetComponent = child.AddComponent<ModularAvatarMenuInstallTarget>();
targetComponent.installer = installer;

EditorGUIUtility.PingObject(child);
}
}

serializedObject.ApplyModifiedProperties();
VirtualMenu.InvalidateCaches();
Repaint();
});
OpenSelectMenu(avatar, installTo);
}
}

Expand Down Expand Up @@ -371,6 +327,78 @@ protected override void OnInnerInspectorGUI()
ShowLanguageUI();
}

private void OpenSelectMenu(VRCAvatarDescriptor avatar, SerializedProperty installTo)
{
AvMenuTreeViewWindow.Show(avatar, _installer, menu =>
{
if (InstallTargets.Count != 1 || menu == InstallTargets[0]) return;

if (InstallTargets[0] is ModularAvatarMenuInstallTarget oldTarget && oldTarget != null)
{
DestroyInstallTargets();
}

if (menu is ValueTuple<object, object> vt) // TODO: This should be a named type...
{
// Menu, ContextCallback
menu = vt.Item1;
}

if (menu is ModularAvatarMenuItem item)
{
if (item.MenuSource == SubmenuSource.MenuAsset)
{
menu = item.Control.subMenu;
}
else
{
var menuParent = item.menuSource_otherObjectChildren != null
? item.menuSource_otherObjectChildren
: item.gameObject;

menu = new MenuNodesUnder(menuParent);
}
}
else if (menu is ModularAvatarMenuGroup group)
{
if (group.targetObject != null) menu = new MenuNodesUnder(group.targetObject);
else menu = new MenuNodesUnder(group.gameObject);
}

if (menu is VRCExpressionsMenu expMenu)
{
if (expMenu == avatar.expressionsMenu) installTo.objectReferenceValue = null;
else installTo.objectReferenceValue = expMenu;
}
else if (menu is RootMenu)
{
installTo.objectReferenceValue = null;
}
else if (menu is MenuNodesUnder nodesUnder)
{
installTo.objectReferenceValue = null;

foreach (var target in targets.Cast<Component>().OrderBy(ObjectHierarchyOrder))
{
var installer = (ModularAvatarMenuInstaller)target;
var child = new GameObject();
Undo.RegisterCreatedObjectUndo(child, "Set install target");
child.transform.SetParent(nodesUnder.root.transform, false);
child.name = installer.gameObject.name;

var targetComponent = child.AddComponent<ModularAvatarMenuInstallTarget>();
targetComponent.installer = installer;

EditorGUIUtility.PingObject(child);
}
}

serializedObject.ApplyModifiedProperties();
VirtualMenu.InvalidateCaches();
Repaint();
});
}

private string ObjectHierarchyOrder(Component arg)
{
var list = new List<int>();
Expand Down
12 changes: 12 additions & 0 deletions Runtime/Menu/ModularAvatarMenuInstaller.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#if MA_VRCSDK3_AVATARS

using System;
using JetBrains.Annotations;
using UnityEngine;
using VRC.SDK3.Avatars.ScriptableObjects;

Expand All @@ -12,6 +14,16 @@ public class ModularAvatarMenuInstaller : AvatarTagComponent
public VRCExpressionsMenu menuToAppend;
public VRCExpressionsMenu installTargetMenu;

internal static Action<ModularAvatarMenuInstaller> _openSelectMenu = _ => { };

/// <summary>
/// Opens the "Select Menu" window, as if the user had clicked this button in the inspector.
/// </summary>
[PublicAPI]
public void OpenSelectMenu()
{
_openSelectMenu(this);
}

// ReSharper disable once Unity.RedundantEventFunction
void Start()
Expand Down

0 comments on commit 14e814d

Please sign in to comment.