2022-10-04 17:34:04 -07:00
|
|
|
|
using System;
|
2022-10-16 12:47:51 -07:00
|
|
|
|
using System.Collections.Generic;
|
2022-12-02 00:01:28 +09:00
|
|
|
|
using System.Linq;
|
2022-10-04 17:34:04 -07:00
|
|
|
|
using UnityEditor;
|
|
|
|
|
using UnityEngine;
|
2022-10-16 12:47:51 -07:00
|
|
|
|
using VRC.SDK3.Avatars.Components;
|
2022-10-04 17:34:04 -07:00
|
|
|
|
using VRC.SDK3.Avatars.ScriptableObjects;
|
2022-11-10 20:39:58 -08:00
|
|
|
|
using static nadena.dev.modular_avatar.core.editor.Localization;
|
2022-12-03 13:32:40 +09:00
|
|
|
|
using static nadena.dev.modular_avatar.core.editor.Util;
|
2022-12-02 00:01:28 +09:00
|
|
|
|
using static nadena.dev.modular_avatar.core.ModularAvatarMenuFolderCreator;
|
|
|
|
|
using Object = UnityEngine.Object;
|
2022-10-04 17:34:04 -07:00
|
|
|
|
|
2022-11-10 20:39:58 -08:00
|
|
|
|
namespace nadena.dev.modular_avatar.core.editor
|
2022-10-04 17:34:04 -07:00
|
|
|
|
{
|
|
|
|
|
[CustomEditor(typeof(ModularAvatarMenuInstaller))]
|
|
|
|
|
[CanEditMultipleObjects]
|
2022-11-10 20:30:10 -08:00
|
|
|
|
internal class MenuInstallerEditor : MAEditorBase
|
2022-10-04 17:34:04 -07:00
|
|
|
|
{
|
|
|
|
|
private ModularAvatarMenuInstaller _installer;
|
|
|
|
|
private Editor _innerMenuEditor;
|
|
|
|
|
private VRCExpressionsMenu _menuToAppend;
|
|
|
|
|
|
|
|
|
|
private bool _menuFoldout;
|
|
|
|
|
private bool _devFoldout;
|
|
|
|
|
|
2022-10-16 12:47:51 -07:00
|
|
|
|
private HashSet<VRCExpressionsMenu> _avatarMenus;
|
2022-12-02 00:01:28 +09:00
|
|
|
|
private HashSet<ModularAvatarMenuFolderCreator> _menuFolderCreators;
|
2022-10-16 12:47:51 -07:00
|
|
|
|
|
2022-10-04 17:34:04 -07:00
|
|
|
|
private void OnEnable()
|
|
|
|
|
{
|
|
|
|
|
_installer = (ModularAvatarMenuInstaller) target;
|
2022-10-16 12:47:51 -07:00
|
|
|
|
|
|
|
|
|
FindMenus();
|
2022-12-02 00:01:28 +09:00
|
|
|
|
FindMenuFolderCreators();
|
2022-10-04 17:34:04 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SetupMenuEditor()
|
|
|
|
|
{
|
|
|
|
|
if (targets.Length != 1)
|
|
|
|
|
{
|
|
|
|
|
_innerMenuEditor = null;
|
|
|
|
|
_menuToAppend = null;
|
|
|
|
|
}
|
|
|
|
|
else if (_installer.menuToAppend != _menuToAppend)
|
|
|
|
|
{
|
|
|
|
|
if (_installer.menuToAppend == null) _innerMenuEditor = null;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_innerMenuEditor = CreateEditor(_installer.menuToAppend);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_menuToAppend = _installer.menuToAppend;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-11-10 20:30:10 -08:00
|
|
|
|
protected override void OnInnerInspectorGUI()
|
2022-10-04 17:34:04 -07:00
|
|
|
|
{
|
|
|
|
|
SetupMenuEditor();
|
2022-12-02 00:01:28 +09:00
|
|
|
|
|
2022-11-25 15:40:29 -08:00
|
|
|
|
VRCAvatarDescriptor commonAvatar = FindCommonAvatar();
|
|
|
|
|
|
2022-12-02 00:01:28 +09:00
|
|
|
|
SerializedProperty installTargetTypeProperty = serializedObject.FindProperty(nameof(ModularAvatarMenuInstaller.InstallTargetType));
|
|
|
|
|
EditorGUILayout.PropertyField(installTargetTypeProperty, new GUIContent("Install Target Type"));
|
|
|
|
|
InstallTargetType installTargetType = (InstallTargetType)Enum.ToObject(typeof(InstallTargetType), installTargetTypeProperty.enumValueIndex);
|
|
|
|
|
|
|
|
|
|
if (!installTargetTypeProperty.hasMultipleDifferentValues)
|
2022-10-16 12:47:51 -07:00
|
|
|
|
{
|
2022-12-02 00:01:28 +09:00
|
|
|
|
string installTargetMenuPropertyName;
|
|
|
|
|
Type installTargetObjectType;
|
|
|
|
|
if (installTargetType == InstallTargetType.VRCExpressionMenu)
|
2022-10-16 12:47:51 -07:00
|
|
|
|
{
|
2022-12-02 00:01:28 +09:00
|
|
|
|
installTargetMenuPropertyName = nameof(ModularAvatarMenuFolderCreator.installTargetMenu);
|
|
|
|
|
installTargetObjectType = typeof(VRCExpressionsMenu);
|
|
|
|
|
} else
|
2022-10-16 12:47:51 -07:00
|
|
|
|
{
|
2022-12-02 00:01:28 +09:00
|
|
|
|
installTargetMenuPropertyName = nameof(ModularAvatarMenuFolderCreator.installTargetFolderCreator);
|
|
|
|
|
installTargetObjectType = typeof(ModularAvatarMenuFolderCreator);
|
|
|
|
|
commonAvatar = null;
|
2022-10-16 12:47:51 -07:00
|
|
|
|
}
|
|
|
|
|
|
2022-12-02 00:01:28 +09:00
|
|
|
|
SerializedProperty installTargetProperty = this.serializedObject.FindProperty(installTargetMenuPropertyName);
|
|
|
|
|
this.ShowMenuInstallerHelpBox(installTargetProperty, installTargetType);
|
|
|
|
|
this.ShowInstallTargetPropertyField(installTargetProperty, commonAvatar, installTargetObjectType);
|
2022-11-25 15:40:29 -08:00
|
|
|
|
|
2022-12-02 00:01:28 +09:00
|
|
|
|
var avatar = RuntimeUtil.FindAvatarInParents(_installer.transform);
|
|
|
|
|
if (avatar != null && GUILayout.Button(G("menuinstall.selectmenu")))
|
2022-11-25 15:40:29 -08:00
|
|
|
|
{
|
2022-12-02 00:01:28 +09:00
|
|
|
|
if (installTargetType == InstallTargetType.VRCExpressionMenu)
|
|
|
|
|
{
|
|
|
|
|
AvMenuTreeViewWindow.Show(avatar, menu =>
|
|
|
|
|
{
|
|
|
|
|
installTargetProperty.objectReferenceValue = menu;
|
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
AvMenuFolderCreatorTreeViewWindow.Show(avatar, null, creator =>
|
|
|
|
|
{
|
|
|
|
|
installTargetProperty.objectReferenceValue = creator;
|
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-11-25 15:40:29 -08:00
|
|
|
|
}
|
2022-12-02 00:01:28 +09:00
|
|
|
|
|
2022-10-04 17:34:04 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (targets.Length == 1)
|
|
|
|
|
{
|
2022-10-16 14:05:26 -07:00
|
|
|
|
_menuFoldout = EditorGUILayout.Foldout(_menuFoldout, G("menuinstall.showcontents"));
|
2022-10-04 17:34:04 -07:00
|
|
|
|
if (_menuFoldout)
|
|
|
|
|
{
|
|
|
|
|
EditorGUI.indentLevel++;
|
|
|
|
|
using (var disabled = new EditorGUI.DisabledScope(true))
|
|
|
|
|
{
|
|
|
|
|
if (_innerMenuEditor != null) _innerMenuEditor.OnInspectorGUI();
|
2022-10-16 14:05:26 -07:00
|
|
|
|
else EditorGUILayout.HelpBox(S("menuinstall.showcontents.notselected"), MessageType.Info);
|
2022-10-04 17:34:04 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EditorGUI.indentLevel--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-16 14:05:26 -07:00
|
|
|
|
_devFoldout = EditorGUILayout.Foldout(_devFoldout, G("menuinstall.devoptions"));
|
2022-12-03 13:32:40 +09:00
|
|
|
|
if (_devFoldout)
|
2022-10-04 17:34:04 -07:00
|
|
|
|
{
|
2022-12-03 13:32:40 +09:00
|
|
|
|
SerializedProperty menuToAppendProperty = serializedObject.FindProperty(nameof(ModularAvatarMenuInstaller.menuToAppend));
|
|
|
|
|
switch (ValidateExpressionMenuIcon((VRCExpressionsMenu)menuToAppendProperty.objectReferenceValue))
|
|
|
|
|
{
|
|
|
|
|
case ValidateExpressionMenuIconResult.Success:
|
|
|
|
|
break;
|
|
|
|
|
case ValidateExpressionMenuIconResult.TooLarge:
|
|
|
|
|
EditorGUILayout.HelpBox(S("menuinstall.menu_icon_too_large"), MessageType.Error);
|
|
|
|
|
break;
|
|
|
|
|
case ValidateExpressionMenuIconResult.Uncompressed:
|
|
|
|
|
EditorGUILayout.HelpBox(S("menuinstall.menu_icon_uncompressed"), MessageType.Error);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-04 17:34:04 -07:00
|
|
|
|
EditorGUI.indentLevel++;
|
|
|
|
|
EditorGUILayout.PropertyField(
|
2022-12-03 13:32:40 +09:00
|
|
|
|
menuToAppendProperty, new GUIContent(G("menuinstall.srcmenu")));
|
2022-10-04 17:34:04 -07:00
|
|
|
|
EditorGUI.indentLevel--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
2022-10-16 14:05:26 -07:00
|
|
|
|
|
|
|
|
|
Localization.ShowLanguageUI();
|
2022-10-04 17:34:04 -07:00
|
|
|
|
}
|
2022-12-02 00:01:28 +09:00
|
|
|
|
|
|
|
|
|
private void ShowMenuInstallerHelpBox(SerializedProperty installTargetProperty, InstallTargetType installTargetType)
|
|
|
|
|
{
|
|
|
|
|
if (installTargetProperty.hasMultipleDifferentValues) return;
|
|
|
|
|
bool isEnabled = targets.Length != 1 || this._installer.enabled;
|
|
|
|
|
|
|
|
|
|
if (installTargetProperty.objectReferenceValue == null)
|
|
|
|
|
{
|
|
|
|
|
if (!isEnabled) return;
|
|
|
|
|
EditorGUILayout.HelpBox(S("menuinstall.help.hint_set_menu"), MessageType.Info);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
VRCAvatarDescriptor avatar = RuntimeUtil.FindAvatarInParents(this._installer.transform);
|
|
|
|
|
switch (installTargetType)
|
|
|
|
|
{
|
|
|
|
|
case InstallTargetType.VRCExpressionMenu:
|
|
|
|
|
if (!this.IsMenuReachable(avatar, (VRCExpressionsMenu)installTargetProperty.objectReferenceValue))
|
|
|
|
|
{
|
|
|
|
|
EditorGUILayout.HelpBox(Localization.S("menuinstall.help.hint_bad_menu"), MessageType.Error);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case InstallTargetType.FolderCreator:
|
|
|
|
|
if (!this.IsMenuReachable(avatar, (ModularAvatarMenuFolderCreator)installTargetProperty.objectReferenceValue, new HashSet<ModularAvatarMenuFolderCreator>()))
|
|
|
|
|
{
|
|
|
|
|
EditorGUILayout.HelpBox("選択されたメニューフォルダからアバターまでのパスが見つかりません。", MessageType.Error);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(installTargetType), installTargetType, null);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ShowInstallTargetPropertyField(SerializedProperty installTargetProperty, VRCAvatarDescriptor avatar, Type propertyType)
|
|
|
|
|
{
|
|
|
|
|
Object displayValue = installTargetProperty.objectReferenceValue;
|
|
|
|
|
if (!installTargetProperty.hasMultipleDifferentValues && avatar != null)
|
|
|
|
|
{
|
|
|
|
|
if (displayValue == null) displayValue = avatar.expressionsMenu;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
|
|
|
Object newValue = EditorGUILayout.ObjectField(G("menuinstall.installto"), displayValue, propertyType,
|
|
|
|
|
propertyType == typeof(ModularAvatarMenuFolderCreator));
|
|
|
|
|
if (EditorGUI.EndChangeCheck())
|
|
|
|
|
{
|
|
|
|
|
installTargetProperty.objectReferenceValue = newValue;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-10-16 12:47:51 -07:00
|
|
|
|
|
2022-11-25 15:40:29 -08:00
|
|
|
|
private VRCAvatarDescriptor FindCommonAvatar()
|
|
|
|
|
{
|
|
|
|
|
VRCAvatarDescriptor commonAvatar = null;
|
|
|
|
|
|
|
|
|
|
foreach (var target in targets)
|
|
|
|
|
{
|
|
|
|
|
var component = (ModularAvatarMenuInstaller) target;
|
|
|
|
|
var avatar = RuntimeUtil.FindAvatarInParents(component.transform);
|
|
|
|
|
if (avatar == null) return null;
|
|
|
|
|
|
|
|
|
|
if (commonAvatar == null)
|
|
|
|
|
{
|
|
|
|
|
commonAvatar = avatar;
|
|
|
|
|
}
|
|
|
|
|
else if (commonAvatar != avatar)
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return commonAvatar;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-16 12:47:51 -07:00
|
|
|
|
private void FindMenus()
|
|
|
|
|
{
|
|
|
|
|
if (targets.Length > 1)
|
|
|
|
|
{
|
|
|
|
|
_avatarMenus = null;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_avatarMenus = new HashSet<VRCExpressionsMenu>();
|
|
|
|
|
var queue = new Queue<VRCExpressionsMenu>();
|
|
|
|
|
var avatar = RuntimeUtil.FindAvatarInParents(((Component) target).transform);
|
|
|
|
|
if (avatar == null || avatar.expressionsMenu == null) return;
|
|
|
|
|
queue.Enqueue(avatar.expressionsMenu);
|
|
|
|
|
|
|
|
|
|
while (queue.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
var menu = queue.Dequeue();
|
|
|
|
|
if (_avatarMenus.Contains(menu)) continue;
|
|
|
|
|
|
|
|
|
|
_avatarMenus.Add(menu);
|
|
|
|
|
foreach (var subMenu in menu.controls)
|
|
|
|
|
{
|
|
|
|
|
if (subMenu.type == VRCExpressionsMenu.Control.ControlType.SubMenu)
|
|
|
|
|
{
|
|
|
|
|
queue.Enqueue(subMenu.subMenu);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-02 00:01:28 +09:00
|
|
|
|
private void FindMenuFolderCreators()
|
|
|
|
|
{
|
|
|
|
|
if (this.targets.Length > 1)
|
|
|
|
|
{
|
|
|
|
|
this._menuFolderCreators = null;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this._menuFolderCreators = new HashSet<ModularAvatarMenuFolderCreator>();
|
|
|
|
|
VRCAvatarDescriptor avatar = RuntimeUtil.FindAvatarInParents(this._installer.transform);
|
|
|
|
|
if (avatar == null) return;
|
|
|
|
|
foreach (ModularAvatarMenuFolderCreator creator in avatar.gameObject.GetComponentsInChildren<ModularAvatarMenuFolderCreator>())
|
|
|
|
|
{
|
|
|
|
|
this._menuFolderCreators.Add(creator);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-16 12:47:51 -07:00
|
|
|
|
private bool IsMenuReachable(VRCAvatarDescriptor avatar, VRCExpressionsMenu menu)
|
|
|
|
|
{
|
|
|
|
|
return _avatarMenus == null || _avatarMenus.Contains(menu);
|
|
|
|
|
}
|
2022-12-03 13:32:40 +09:00
|
|
|
|
|
2022-12-02 00:01:28 +09:00
|
|
|
|
private bool IsMenuReachable(VRCAvatarDescriptor avatar, ModularAvatarMenuFolderCreator creator, HashSet<ModularAvatarMenuFolderCreator> session)
|
|
|
|
|
{
|
|
|
|
|
if (avatar == null) return true;
|
|
|
|
|
if (this._menuFolderCreators == null) return true;
|
|
|
|
|
|
|
|
|
|
if (session.Contains(creator)) return false;
|
|
|
|
|
if (!this._menuFolderCreators.Contains(creator)) return false;
|
|
|
|
|
|
|
|
|
|
if (!creator.enabled) return false;
|
|
|
|
|
session.Add(creator);
|
|
|
|
|
switch (creator.installTargetType)
|
|
|
|
|
{
|
|
|
|
|
case InstallTargetType.VRCExpressionMenu:
|
|
|
|
|
return creator.installTargetMenu == null || this.IsMenuReachable(avatar, creator.installTargetMenu);
|
|
|
|
|
case InstallTargetType.FolderCreator:
|
|
|
|
|
return creator.installTargetFolderCreator == null || this.IsMenuReachable(avatar, creator.installTargetFolderCreator, session);
|
|
|
|
|
default:
|
|
|
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-03 13:32:40 +09:00
|
|
|
|
private static ValidateExpressionMenuIconResult ValidateExpressionMenuIcon(VRCExpressionsMenu menu)
|
|
|
|
|
{
|
|
|
|
|
if (menu == null) return ValidateExpressionMenuIconResult.Success;
|
|
|
|
|
|
|
|
|
|
foreach (VRCExpressionsMenu.Control control in menu.controls)
|
|
|
|
|
{
|
|
|
|
|
// Control
|
|
|
|
|
ValidateExpressionMenuIconResult result = Util.ValidateExpressionMenuIcon(control.icon);
|
|
|
|
|
if (result != ValidateExpressionMenuIconResult.Success) return result;
|
|
|
|
|
|
|
|
|
|
// Labels
|
|
|
|
|
foreach (VRCExpressionsMenu.Control.Label label in control.labels)
|
|
|
|
|
{
|
|
|
|
|
ValidateExpressionMenuIconResult labelResult = Util.ValidateExpressionMenuIcon(label.icon);
|
|
|
|
|
if (labelResult != ValidateExpressionMenuIconResult.Success) return labelResult;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SubMenu
|
|
|
|
|
if (control.type != VRCExpressionsMenu.Control.ControlType.SubMenu) continue;
|
|
|
|
|
ValidateExpressionMenuIconResult subMenuResult = ValidateExpressionMenuIcon(control.subMenu);
|
|
|
|
|
if (subMenuResult != ValidateExpressionMenuIconResult.Success) return subMenuResult;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ValidateExpressionMenuIconResult.Success;
|
2022-12-02 00:01:28 +09:00
|
|
|
|
|
|
|
|
|
|
2022-10-04 17:34:04 -07:00
|
|
|
|
}
|
|
|
|
|
}
|