mirror of
https://github.com/bdunderscore/modular-avatar.git
synced 2024-12-28 10:15:06 +08:00
change: MA Info is now a normal editor window (#850)
This commit is contained in:
parent
61a70feed7
commit
bc12e3a985
@ -22,25 +22,29 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#region
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using nadena.dev.modular_avatar.ui;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
[assembly: InternalsVisibleTo("Tests")]
|
||||
|
||||
namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
public class AvatarProcessor
|
||||
{
|
||||
[MenuItem("GameObject/ModularAvatar/Manual bake avatar", true, 100)]
|
||||
[MenuItem(UnityMenuItems.GameObject_ManualBake, true, UnityMenuItems.GameObject_ManualBakeOrder)]
|
||||
static bool ValidateApplyToCurrentAvatarGameobject()
|
||||
{
|
||||
return ValidateApplyToCurrentAvatar();
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/ModularAvatar/Manual bake avatar", false, 100)]
|
||||
[MenuItem(UnityMenuItems.GameObject_ManualBake, false, UnityMenuItems.GameObject_ManualBakeOrder)]
|
||||
static void ApplyToCurrentAvatarGameobject()
|
||||
{
|
||||
ApplyToCurrentAvatar();
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using nadena.dev.modular_avatar.ui;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
@ -114,11 +115,10 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
|
||||
internal static class EasySetupOutfit
|
||||
{
|
||||
private const int PRIORITY = 49;
|
||||
private static string[] errorMessageGroups;
|
||||
private static string errorHeader;
|
||||
|
||||
[MenuItem("GameObject/ModularAvatar/Setup Outfit", false, PRIORITY)]
|
||||
[MenuItem(UnityMenuItems.GameObject_SetupOutfit, false, UnityMenuItems.GameObject_SetupOutfitOrder)]
|
||||
internal static void SetupOutfit(MenuCommand cmd)
|
||||
{
|
||||
if (!ValidateSetupOutfit())
|
||||
|
@ -1,102 +0,0 @@
|
||||
#region
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using HarmonyLib;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace nadena.dev.modular_avatar.core.editor.HarmonyPatches
|
||||
{
|
||||
internal static class InjectParamsUsageUI
|
||||
{
|
||||
private static readonly Type type = AccessTools.TypeByName("UnityEditor.PropertyEditor");
|
||||
private static readonly PropertyInfo _editorsElement = AccessTools.Property(type, "editorsElement");
|
||||
|
||||
private static readonly Type editorElem = AccessTools.TypeByName("UnityEditor.UIElements.EditorElement");
|
||||
private static readonly PropertyInfo editorElem_editor = AccessTools.Property(editorElem, "editor");
|
||||
|
||||
public static void Patch(Harmony h)
|
||||
{
|
||||
var type = AccessTools.TypeByName("UnityEditor.PropertyEditor");
|
||||
var drawEditors = AccessTools.Method(type, "DrawEditors");
|
||||
|
||||
h.Patch(drawEditors, transpiler: new HarmonyMethod(typeof(InjectParamsUsageUI), nameof(Transpile)));
|
||||
|
||||
var objNames = AccessTools.TypeByName("UnityEditor.ObjectNames");
|
||||
var m_GetObjectTypeName = AccessTools.Method(objNames, "GetObjectTypeName");
|
||||
var postfix_GetObjectTypeName =
|
||||
AccessTools.Method(typeof(InjectParamsUsageUI), nameof(Postfix_GetObjectTypeName));
|
||||
|
||||
h.Patch(m_GetObjectTypeName, postfix: new HarmonyMethod(postfix_GetObjectTypeName));
|
||||
}
|
||||
|
||||
private static void Postfix_GetObjectTypeName(ref string __result, Object o)
|
||||
{
|
||||
if (o is ModularAvatarInformation)
|
||||
{
|
||||
__result = "Modular Avatar Information";
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<CodeInstruction> Transpile(IEnumerable<CodeInstruction> ci)
|
||||
{
|
||||
var target = AccessTools.Method(typeof(VisualElement), "Add");
|
||||
|
||||
foreach (var i in ci)
|
||||
{
|
||||
if (i.opcode != OpCodes.Callvirt)
|
||||
{
|
||||
yield return i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i.opcode == OpCodes.Callvirt
|
||||
&& i.operand is MethodInfo method
|
||||
&& method == target
|
||||
)
|
||||
{
|
||||
yield return new CodeInstruction(OpCodes.Ldarg_0);
|
||||
yield return new CodeInstruction(OpCodes.Call,
|
||||
AccessTools.Method(typeof(InjectParamsUsageUI), nameof(EditorAdd)));
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return i;
|
||||
}
|
||||
}
|
||||
|
||||
private static void EditorAdd(VisualElement container, VisualElement child, object caller)
|
||||
{
|
||||
container.Add(child);
|
||||
|
||||
var editorsElement = _editorsElement.GetValue(caller) as VisualElement;
|
||||
if (editorsElement != container)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!child.ClassListContains("game-object-inspector"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var editor = editorElem_editor.GetValue(child) as Editor;
|
||||
if (editor == null) return;
|
||||
|
||||
if (editor.targets.Length != 1) return;
|
||||
|
||||
if (editor.target is GameObject obj)
|
||||
{
|
||||
var elem = new ParamsUsageUI();
|
||||
container.Add(elem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5d62a8f41641443ea8bffdc0429e0ad1
|
||||
timeCreated: 1710223876
|
@ -17,7 +17,6 @@ namespace nadena.dev.modular_avatar.core.editor.HarmonyPatches
|
||||
#if UNITY_2022_3_OR_NEWER
|
||||
HandleUtilityPatches.Patch_FilterInstanceIDs,
|
||||
PickingObjectPatch.Patch,
|
||||
InjectParamsUsageUI.Patch,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -1,11 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
#region
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
public class LogoElement : VisualElement
|
||||
@ -85,19 +87,8 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
|
||||
public LogoElement()
|
||||
{
|
||||
_inner = new VisualElement();
|
||||
|
||||
_inner = new LogoImage();
|
||||
_inner.style.display = DisplayStyle.None;
|
||||
_inner.style.flexDirection = FlexDirection.Row;
|
||||
_inner.style.alignItems = Align.Center;
|
||||
_inner.style.justifyContent = Justify.Center;
|
||||
|
||||
var image = new Image();
|
||||
image.image = LogoDisplay.LOGO_ASSET;
|
||||
image.style.width = new Length(LogoDisplay.ImageWidth(LogoDisplay.TARGET_HEIGHT), LengthUnit.Pixel);
|
||||
image.style.height = new Length(LogoDisplay.TARGET_HEIGHT, LengthUnit.Pixel);
|
||||
|
||||
_inner.Add(image);
|
||||
this.Add(_inner);
|
||||
|
||||
RegisterCallback<GeometryChangedEvent>(OnGeomChanged);
|
||||
|
38
Editor/Inspector/Common/LogoImage.cs
Normal file
38
Editor/Inspector/Common/LogoImage.cs
Normal file
@ -0,0 +1,38 @@
|
||||
#region
|
||||
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
public class LogoImage : VisualElement
|
||||
{
|
||||
VisualElement _inner;
|
||||
|
||||
public new class UxmlFactory : UxmlFactory<LogoImage, UxmlTraits>
|
||||
{
|
||||
}
|
||||
|
||||
public new class UxmlTraits : VisualElement.UxmlTraits
|
||||
{
|
||||
}
|
||||
|
||||
public LogoImage()
|
||||
{
|
||||
_inner = new VisualElement();
|
||||
|
||||
_inner.style.flexDirection = FlexDirection.Row;
|
||||
_inner.style.alignItems = Align.Center;
|
||||
_inner.style.justifyContent = Justify.Center;
|
||||
|
||||
var image = new Image();
|
||||
image.image = LogoDisplay.LOGO_ASSET;
|
||||
image.style.width = new Length(LogoDisplay.ImageWidth(LogoDisplay.TARGET_HEIGHT), LengthUnit.Pixel);
|
||||
image.style.height = new Length(LogoDisplay.TARGET_HEIGHT, LengthUnit.Pixel);
|
||||
|
||||
_inner.Add(image);
|
||||
Add(_inner);
|
||||
}
|
||||
}
|
||||
}
|
3
Editor/Inspector/Common/LogoImage.cs.meta
Normal file
3
Editor/Inspector/Common/LogoImage.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d12c8166c3f14b78a76dbef22b07fad1
|
||||
timeCreated: 1715480586
|
@ -1,7 +1,11 @@
|
||||
using System;
|
||||
#region
|
||||
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
internal static class LogoDisplay
|
||||
@ -37,7 +41,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
|
||||
internal static void DisplayLogo()
|
||||
{
|
||||
if (LOGO_ASSET == null) return;
|
||||
if (LOGO_ASSET == null || EditorStyles.label == null) return;
|
||||
|
||||
var height = TARGET_HEIGHT;
|
||||
var width = ImageWidth(height);
|
||||
|
@ -246,5 +246,6 @@
|
||||
"ma_info.param_usage_ui.header": "Expressions Parameter Usage",
|
||||
"ma_info.param_usage_ui.other_objects": "Other objects on this avatar",
|
||||
"ma_info.param_usage_ui.free_space": "Unused parameter space ({0} bits)",
|
||||
"ma_info.param_usage_ui.bits_template": "{0} ({1} bits)"
|
||||
"ma_info.param_usage_ui.bits_template": "{0} ({1} bits)",
|
||||
"ma_info.param_usage_ui.no_data": "[ NO DATA ]"
|
||||
}
|
@ -242,5 +242,6 @@
|
||||
"ma_info.param_usage_ui.header": "Expressions Parameter 使用状況",
|
||||
"ma_info.param_usage_ui.other_objects": "このアバター内の他のオブジェクト",
|
||||
"ma_info.param_usage_ui.free_space": "未使用領域 ({0} 個のビット)",
|
||||
"ma_info.param_usage_ui.bits_template": "{0} ({1} 個のビットを使用中)"
|
||||
"ma_info.param_usage_ui.bits_template": "{0} ({1} 個のビットを使用中)",
|
||||
"ma_info.param_usage_ui.no_data": "[ NO DATA ]"
|
||||
}
|
@ -1,19 +1,23 @@
|
||||
#if MA_VRCSDK3_AVATARS
|
||||
|
||||
#region
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using nadena.dev.modular_avatar.ui;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using VRC.SDK3.Avatars.Components;
|
||||
using VRC.SDK3.Avatars.ScriptableObjects;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
internal class MenuExtractor
|
||||
{
|
||||
private const int PRIORITY = 49;
|
||||
|
||||
[MenuItem("GameObject/ModularAvatar/Extract menu", false, PRIORITY)]
|
||||
[MenuItem(UnityMenuItems.GameObject_ExtractMenu, false, UnityMenuItems.GameObject_ExtractMenuOrder)]
|
||||
static void ExtractMenu(MenuCommand menuCommand)
|
||||
{
|
||||
if (!(menuCommand.context is GameObject gameObj)) return;
|
||||
@ -32,10 +36,10 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
|
||||
var assetPath = AssetDatabase.GetAssetPath(avatar.expressionsMenu);
|
||||
var dummyAssetPathBase = assetPath.Replace(".asset", " placeholder");
|
||||
if (dummyAssetPathBase.StartsWith("Packages" + System.IO.Path.DirectorySeparatorChar))
|
||||
if (dummyAssetPathBase.StartsWith("Packages" + Path.DirectorySeparatorChar))
|
||||
{
|
||||
var filename = System.IO.Path.GetFileName(dummyAssetPathBase);
|
||||
dummyAssetPathBase = System.IO.Path.Combine("Assets", filename);
|
||||
var filename = Path.GetFileName(dummyAssetPathBase);
|
||||
dummyAssetPathBase = Path.Combine("Assets", filename);
|
||||
}
|
||||
|
||||
// Check that a similarly-named file doesn't already exist
|
||||
@ -43,7 +47,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
do
|
||||
{
|
||||
var fullPath = dummyAssetPathBase + (i > 0 ? " " + i : "") + ".asset";
|
||||
if (System.IO.File.Exists(fullPath))
|
||||
if (File.Exists(fullPath))
|
||||
{
|
||||
var asset = AssetDatabase.LoadAssetAtPath<VRCExpressionsMenu>(fullPath);
|
||||
if (asset != null && asset.controls.Count == 0)
|
||||
@ -52,7 +56,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (!System.IO.File.Exists(fullPath))
|
||||
else if (!File.Exists(fullPath))
|
||||
{
|
||||
var dummyAsset = ScriptableObject.CreateInstance<VRCExpressionsMenu>();
|
||||
AssetDatabase.CreateAsset(dummyAsset, fullPath);
|
||||
|
@ -1,23 +0,0 @@
|
||||
#region
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
[HelpURL("https://m-a.nadena.dev/docs/intro?lang=auto")]
|
||||
internal class ModularAvatarInformation : ScriptableObject
|
||||
{
|
||||
internal static ModularAvatarInformation _instance;
|
||||
|
||||
internal static ModularAvatarInformation instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null) _instance = CreateInstance<ModularAvatarInformation>();
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f902feee12ad4fcbb8a975bbea565ab1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: a8edd5bd1a0a64a40aa99cc09fb5f198, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -5,10 +5,12 @@
|
||||
|
||||
#Outerbox {
|
||||
margin-top: 4px;
|
||||
margin-bottom: 8px;
|
||||
|
||||
border-top-width: 1px;
|
||||
border-bottom-width: 1px;
|
||||
border-left-width: 1px;
|
||||
border-right-width: 1px;
|
||||
/*border-left-width: 1px;
|
||||
border-right-width: 1px;*/
|
||||
border-color: black;
|
||||
|
||||
padding: 4px;
|
||||
@ -18,6 +20,11 @@
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
#Footer {
|
||||
margin-left: 15px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
#UsageBox {
|
||||
height: 16px;
|
||||
flex-direction: row;
|
||||
@ -72,4 +79,33 @@
|
||||
margin-right: -4px;
|
||||
border-left-width: 4px;
|
||||
border-right-width: 4px;
|
||||
}
|
||||
|
||||
/* NO DATA display */
|
||||
#Outerbox Toggle {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Why doesn't #Outerbox.no-data > .unity-foldout__content > * work here? */
|
||||
#Outerbox.no-data #UsageBox {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#Outerbox.no-data #Legend {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#NoData {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#Outerbox.no-data #NoData {
|
||||
font-size: 150%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
@ -1,8 +1,12 @@
|
||||
<UXML xmlns:ui="UnityEngine.UIElements" xmlns:ma="nadena.dev.modular_avatar.core.editor">
|
||||
<ui:VisualElement name="root-box">
|
||||
<ui:Label text="ma_info.param_usage_ui.header" class="header ndmf-tr"/>
|
||||
<ma:LogoImage/>
|
||||
|
||||
<ui:Foldout text="ma_info.param_usage_ui.header" class="header ndmf-tr" name="Outerbox">
|
||||
<ui:VisualElement name="NoData">
|
||||
<ui:Label text="ma_info.param_usage_ui.no_data" class="ndmf-tr"/>
|
||||
</ui:VisualElement>
|
||||
|
||||
<ui:VisualElement name="Outerbox">
|
||||
<ui:VisualElement name="UsageBox">
|
||||
<ui:VisualElement name="OtherObjects" style="background-color: #888888; flex-grow: 1;"/>
|
||||
<ui:VisualElement name="UnusedSpace" style="width: auto; background-color: #eeeeee; flex-grow: 1;"/>
|
||||
@ -28,8 +32,10 @@
|
||||
</ui:VisualElement>
|
||||
|
||||
</ui:VisualElement>
|
||||
</ui:VisualElement>
|
||||
</ui:Foldout>
|
||||
|
||||
<ma:LanguageSwitcherElement/>
|
||||
<ui:VisualElement name="Footer">
|
||||
<ma:LanguageSwitcherElement/>
|
||||
</ui:VisualElement>
|
||||
</ui:VisualElement>
|
||||
</UXML>
|
@ -1,40 +0,0 @@
|
||||
using System;
|
||||
using nadena.dev.modular_avatar.ui;
|
||||
using UnityEditor;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
#if UNITY_2022_3_OR_NEWER
|
||||
[FilePath("modular-avatar/ParamsUsagePrefs.asset", FilePathAttribute.Location.PreferencesFolder)]
|
||||
internal sealed class ParamsUsagePrefs : ScriptableSingleton<ParamsUsagePrefs>
|
||||
{
|
||||
public static event Action<bool> OnChange_EnableInfoMenu;
|
||||
|
||||
[FormerlySerializedAs("EnableInfoMenu")] public bool enableInfoMenu = true;
|
||||
|
||||
[MenuItem(UnityMenuItems.TopMenu_EnableInfo, false, UnityMenuItems.TopMenu_EnableInfoOrder)]
|
||||
private static void Menu_EnableInfo()
|
||||
{
|
||||
ParamsUsagePrefs.instance.enableInfoMenu = !ParamsUsagePrefs.instance.enableInfoMenu;
|
||||
Menu.SetChecked(UnityMenuItems.TopMenu_EnableInfo, ParamsUsagePrefs.instance.enableInfoMenu);
|
||||
ParamsUsagePrefs.instance.Save(true);
|
||||
|
||||
OnChange_EnableInfoMenu?.Invoke(ParamsUsagePrefs.instance.enableInfoMenu);
|
||||
}
|
||||
|
||||
[InitializeOnLoadMethod]
|
||||
private static void Initialize()
|
||||
{
|
||||
Menu.SetChecked(UnityMenuItems.TopMenu_EnableInfo, ParamsUsagePrefs.instance.enableInfoMenu);
|
||||
}
|
||||
}
|
||||
#else
|
||||
internal sealed class ParamsUsagePrefs
|
||||
{
|
||||
public static ParamsUsagePrefs instance => new ParamsUsagePrefs();
|
||||
public static event Action<bool> OnChange_EnableInfoMenu;
|
||||
public bool enableInfoMenu => false;
|
||||
}
|
||||
#endif
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3ebf965fd4064a52896def62c36c6a90
|
||||
timeCreated: 1713742583
|
@ -1,220 +0,0 @@
|
||||
#region
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using HarmonyLib;
|
||||
using nadena.dev.ndmf.localization;
|
||||
using UnityEditor;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
internal class ParamsUsageUI : VisualElement
|
||||
{
|
||||
private static readonly Type editorElem = AccessTools.TypeByName("UnityEditor.UIElements.EditorElement");
|
||||
private static readonly PropertyInfo editorElem_editor = AccessTools.Property(editorElem, "editor");
|
||||
|
||||
private class FoldoutState
|
||||
{
|
||||
public bool Visible;
|
||||
}
|
||||
|
||||
private static ConditionalWeakTable<VisualElement, FoldoutState> FoldoutStateHolder =
|
||||
new ConditionalWeakTable<VisualElement, FoldoutState>();
|
||||
|
||||
private VisualElement _gameObjectEditorElement;
|
||||
private Editor _parentEditor;
|
||||
private Object _rawTarget;
|
||||
private GameObject _target;
|
||||
private ParamsUsageEditor _editor;
|
||||
private FoldoutState _foldoutState;
|
||||
|
||||
private bool _recursing = false;
|
||||
|
||||
public ParamsUsageUI()
|
||||
{
|
||||
RegisterCallback<AttachToPanelEvent>(OnAttach);
|
||||
RegisterCallback<DetachFromPanelEvent>(OnDetach);
|
||||
|
||||
LanguagePrefs.RegisterLanguageChangeCallback(this,
|
||||
(self) => self.OnLanguageChangedCallback());
|
||||
}
|
||||
|
||||
private void OnChange_EnableState(bool enableState)
|
||||
{
|
||||
if (_editor != null)
|
||||
{
|
||||
Rebuild();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnLanguageChangedCallback()
|
||||
{
|
||||
if (_editor != null)
|
||||
{
|
||||
Rebuild();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDetach(DetachFromPanelEvent evt)
|
||||
{
|
||||
if (_recursing) return;
|
||||
|
||||
Clear();
|
||||
|
||||
if (_editor != null)
|
||||
{
|
||||
Object.DestroyImmediate(_editor);
|
||||
_editor = null;
|
||||
}
|
||||
|
||||
ParamsUsagePrefs.OnChange_EnableInfoMenu -= OnChange_EnableState;
|
||||
}
|
||||
|
||||
private void OnAttach(AttachToPanelEvent evt)
|
||||
{
|
||||
if (_recursing) return;
|
||||
|
||||
Rebuild();
|
||||
|
||||
ParamsUsagePrefs.OnChange_EnableInfoMenu += OnChange_EnableState;
|
||||
}
|
||||
|
||||
private void Rebuild()
|
||||
{
|
||||
if (parent == null) return;
|
||||
|
||||
if (!ParamsUsagePrefs.instance.enableInfoMenu)
|
||||
{
|
||||
Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
SetRedrawSensor();
|
||||
|
||||
if (_gameObjectEditorElement?.parent != parent)
|
||||
{
|
||||
_gameObjectEditorElement = null;
|
||||
|
||||
var kv = FindEditorElement();
|
||||
if (kv != null)
|
||||
{
|
||||
var elem = kv.Value.Item1;
|
||||
var index = kv.Value.Item2;
|
||||
|
||||
if (index != parent.Children().ToList().IndexOf(this))
|
||||
{
|
||||
_recursing = true;
|
||||
var p = parent;
|
||||
RemoveFromHierarchy();
|
||||
p.Insert(index + 1, this);
|
||||
_recursing = false;
|
||||
}
|
||||
|
||||
_gameObjectEditorElement = elem;
|
||||
}
|
||||
}
|
||||
|
||||
if (_gameObjectEditorElement == null) return;
|
||||
|
||||
_parentEditor = editorElem_editor.GetValue(_gameObjectEditorElement) as Editor;
|
||||
if (_parentEditor == null) return;
|
||||
|
||||
_rawTarget = _parentEditor.target;
|
||||
_target = _rawTarget as GameObject;
|
||||
|
||||
if (_target == null) return;
|
||||
|
||||
Clear();
|
||||
_redrawSensorActive = false;
|
||||
BuildContent();
|
||||
}
|
||||
|
||||
private (VisualElement, int)? FindEditorElement()
|
||||
{
|
||||
foreach (var (elem, index) in parent.Children().Select((e, i) => (e, i)))
|
||||
{
|
||||
if (elem.ClassListContains("game-object-inspector"))
|
||||
{
|
||||
return (elem, index);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool _redrawSensorActive = false;
|
||||
|
||||
private void SetRedrawSensor()
|
||||
{
|
||||
if (_redrawSensorActive) return;
|
||||
|
||||
Clear();
|
||||
_redrawSensorActive = true;
|
||||
Add(new IMGUIContainer(() => EditorApplication.delayCall += Rebuild));
|
||||
}
|
||||
|
||||
private void BuildContent()
|
||||
{
|
||||
Clear();
|
||||
|
||||
if (!FoldoutStateHolder.TryGetValue(parent, out _foldoutState))
|
||||
{
|
||||
_foldoutState = new FoldoutState();
|
||||
FoldoutStateHolder.Add(parent, _foldoutState);
|
||||
}
|
||||
|
||||
if (RuntimeUtil.FindAvatarTransformInParents(_target.transform) == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_editor = Editor.CreateEditorWithContext(new Object[] { ModularAvatarInformation.instance }, _target,
|
||||
typeof(ParamsUsageEditor))
|
||||
as ParamsUsageEditor;
|
||||
|
||||
if (_editor == null) return;
|
||||
|
||||
var inspectorElement = new InspectorElement(_editor);
|
||||
|
||||
Add(new IMGUIContainer(() =>
|
||||
{
|
||||
if (_gameObjectEditorElement?.parent != parent || _parentEditor == null ||
|
||||
_parentEditor.target != _rawTarget)
|
||||
{
|
||||
EditorApplication.delayCall += Rebuild;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (Event.current.rawType)
|
||||
{
|
||||
case EventType.Repaint:
|
||||
case EventType.MouseMove:
|
||||
case EventType.Layout:
|
||||
break;
|
||||
case EventType.MouseDrag:
|
||||
case EventType.DragUpdated:
|
||||
case EventType.DragPerform:
|
||||
case EventType.DragExited:
|
||||
return;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_foldoutState.Visible = EditorGUILayout.InspectorTitlebar(_foldoutState.Visible, _editor);
|
||||
inspectorElement.style.display = _foldoutState.Visible ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
_editor.Visible = _foldoutState.Visible;
|
||||
}));
|
||||
_editor.Visible = _foldoutState.Visible;
|
||||
Add(inspectorElement);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 18364f2754ed43c3baba0f1e18ac03cd
|
||||
timeCreated: 1710226452
|
@ -1,7 +1,9 @@
|
||||
#region
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using nadena.dev.modular_avatar.ui;
|
||||
using nadena.dev.ndmf;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
@ -12,7 +14,7 @@ using VRC.SDK3.Avatars.ScriptableObjects;
|
||||
|
||||
namespace nadena.dev.modular_avatar.core.editor
|
||||
{
|
||||
internal class ParamsUsageEditor : MAEditorBase
|
||||
internal class ParamsUsageWindow : EditorWindow
|
||||
{
|
||||
[SerializeField] private StyleSheet uss;
|
||||
[SerializeField] private VisualTreeAsset uxml;
|
||||
@ -32,11 +34,59 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
if (_visible == value) return;
|
||||
_visible = value;
|
||||
|
||||
if (_visible) Recalculate();
|
||||
#if UNITY_2022_1_OR_NEWER
|
||||
if (_visible)
|
||||
{
|
||||
Recalculate();
|
||||
ObjectChangeEvents.changesPublished += OnChangesPublished;
|
||||
}
|
||||
else
|
||||
{
|
||||
ObjectChangeEvents.changesPublished -= OnChangesPublished;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBecameVisible()
|
||||
{
|
||||
Visible = true;
|
||||
}
|
||||
|
||||
private void OnBecameInvisible()
|
||||
{
|
||||
Visible = false;
|
||||
}
|
||||
|
||||
private void OnSelectionChange()
|
||||
{
|
||||
if (Visible)
|
||||
{
|
||||
Recalculate();
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_2022_1_OR_NEWER
|
||||
[MenuItem(UnityMenuItems.TopMenu_EnableInfo, false, UnityMenuItems.TopMenu_EnableInfoOrder)]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
var window = GetWindow<ParamsUsageWindow>();
|
||||
window.titleContent = new GUIContent("MA Information");
|
||||
window.Show();
|
||||
}
|
||||
|
||||
[MenuItem(UnityMenuItems.GameObject_EnableInfo, false, UnityMenuItems.GameObject_EnableInfoOrder)]
|
||||
public static void ShowWindowFromGameObject()
|
||||
{
|
||||
ShowWindow();
|
||||
}
|
||||
|
||||
[MenuItem(UnityMenuItems.GameObject_EnableInfo, true, UnityMenuItems.GameObject_EnableInfoOrder)]
|
||||
public static bool ValidateShowWindowFromGameObject()
|
||||
{
|
||||
return Selection.gameObjects.Length == 1;
|
||||
}
|
||||
|
||||
private bool _delayPending = false;
|
||||
|
||||
private void DelayRecalculate()
|
||||
@ -47,40 +97,46 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
|
||||
private void OnChangesPublished(ref ObjectChangeEventStream stream)
|
||||
{
|
||||
if (_root == null || !_visible) return;
|
||||
if (!_delayPending) EditorApplication.delayCall += DelayRecalculate;
|
||||
_delayPending = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected override VisualElement CreateInnerInspectorGUI()
|
||||
bool GUIIsReady()
|
||||
{
|
||||
try
|
||||
{
|
||||
return uxml != null && EditorStyles.label != null;
|
||||
}
|
||||
catch (NullReferenceException _)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected void CreateGUI()
|
||||
{
|
||||
if (!GUIIsReady())
|
||||
{
|
||||
// After domain reload, the uxml field (and EditorStyles, etc) isn't initialized immediately.
|
||||
// Try again in the next frame.
|
||||
EditorApplication.delayCall += CreateGUI;
|
||||
return;
|
||||
}
|
||||
|
||||
_root = uxml.CloneTree();
|
||||
|
||||
rootVisualElement.Add(_root);
|
||||
_root.styleSheets.Add(uss);
|
||||
Localization.L.LocalizeUIElements(_root);
|
||||
|
||||
|
||||
_legendContainer = _root.Q<VisualElement>("Legend");
|
||||
_usageBoxContainer = _root.Q<VisualElement>("UsageBox");
|
||||
|
||||
#if UNITY_2022_1_OR_NEWER
|
||||
_root.RegisterCallback<AttachToPanelEvent>(_evt =>
|
||||
{
|
||||
ObjectChangeEvents.changesPublished += OnChangesPublished;
|
||||
Recalculate();
|
||||
});
|
||||
|
||||
_root.RegisterCallback<DetachFromPanelEvent>(_evt =>
|
||||
{
|
||||
ObjectChangeEvents.changesPublished -= OnChangesPublished;
|
||||
});
|
||||
#endif
|
||||
|
||||
return _root;
|
||||
Recalculate();
|
||||
}
|
||||
|
||||
protected override void OnInnerInspectorGUI()
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
private static IEnumerable<Color> Colors()
|
||||
{
|
||||
@ -108,15 +164,25 @@ namespace nadena.dev.modular_avatar.core.editor
|
||||
private void Recalculate()
|
||||
{
|
||||
if (_root == null || !_visible) return;
|
||||
|
||||
var ctx = serializedObject.context as GameObject;
|
||||
|
||||
if (ctx == null) return;
|
||||
var objects = Selection.gameObjects;
|
||||
var target = objects.Length == 1 ? objects[0] : null;
|
||||
var avatarRoot = target != null
|
||||
? RuntimeUtil.FindAvatarTransformInParents(target.transform)?.gameObject
|
||||
: null;
|
||||
|
||||
var avatarRoot = RuntimeUtil.FindAvatarTransformInParents(ctx.transform)?.gameObject;
|
||||
if (avatarRoot == null) return;
|
||||
var outerbox = _root.Q<VisualElement>("Outerbox");
|
||||
if (avatarRoot == null)
|
||||
{
|
||||
outerbox.AddToClassList("no-data");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
outerbox.RemoveFromClassList("no-data");
|
||||
}
|
||||
|
||||
var orderedPlugins = ParameterInfo.ForUI.GetParametersForObject(ctx)
|
||||
var orderedPlugins = ParameterInfo.ForUI.GetParametersForObject(target)
|
||||
.GroupBy(p => p.Plugin)
|
||||
.Select(group => (group.Key, group.Sum(p => p.BitUsage)))
|
||||
.Where((kv, index) => kv.Item2 > 0)
|
@ -1,12 +1,31 @@
|
||||
namespace nadena.dev.modular_avatar.ui
|
||||
{
|
||||
internal class UnityMenuItems
|
||||
internal static class UnityMenuItems
|
||||
{
|
||||
internal const string GameObject_SetupOutfit = "GameObject/Modular Avatar/Setup Outfit";
|
||||
internal const int GameObject_SetupOutfitOrder = -1000;
|
||||
|
||||
internal const string GameObject_ManualBake = "GameObject/Modular Avatar/Manual Bake Avatar";
|
||||
internal const int GameObject_ManualBakeOrder = GameObject_SetupOutfitOrder + 1;
|
||||
|
||||
// <separator>
|
||||
|
||||
internal const string GameObject_EnableInfo = "GameObject/Modular Avatar/Show Modular Avatar Information";
|
||||
internal const int GameObject_EnableInfoOrder = -799;
|
||||
|
||||
internal const string GameObject_ExtractMenu = "GameObject/Modular Avatar/Extract Menu";
|
||||
internal const int GameObject_ExtractMenuOrder = GameObject_EnableInfoOrder + 1;
|
||||
|
||||
|
||||
|
||||
|
||||
internal const string TopMenu_EditModeBoneSync = "Tools/Modular Avatar/Sync Bones in Edit Mode";
|
||||
internal const int TopMenu_EditModeBoneSyncOrder = 100;
|
||||
|
||||
internal const string TopMenu_EnableInfo = "Tools/Modular Avatar/Show Modular Avatar Information";
|
||||
internal const int TopMenu_EnableInfoOrder = 101;
|
||||
|
||||
// <separator>
|
||||
|
||||
internal const string TopMenu_ManualBakeAvatar = "Tools/Modular Avatar/Manual Bake Avatar";
|
||||
internal const int TopMenu_ManualBakeAvatarOrder = 1000;
|
||||
|
Loading…
Reference in New Issue
Block a user