mirror of
https://github.com/bdunderscore/modular-avatar.git
synced 2025-05-14 15:19:11 +08:00
ToggleGroup UI
This commit is contained in:
parent
64d831672a
commit
f2ef8099b0
@ -81,10 +81,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
|
|
||||||
protected virtual VisualElement CreateInnerInspectorGUI()
|
protected virtual VisualElement CreateInnerInspectorGUI()
|
||||||
{
|
{
|
||||||
var throwaway = new InspectorElement();
|
return null;
|
||||||
MethodInfo m = typeof(InspectorElement).GetMethod("CreateIMGUIInspectorFromEditor",
|
|
||||||
BindingFlags.NonPublic | BindingFlags.Instance);
|
|
||||||
return m.Invoke(throwaway, new object[] {serializedObject, this, false}) as VisualElement;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed override VisualElement CreateInspectorGUI()
|
public sealed override VisualElement CreateInspectorGUI()
|
||||||
@ -94,10 +91,19 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
|
|
||||||
var inner = CreateInnerInspectorGUI();
|
var inner = CreateInnerInspectorGUI();
|
||||||
|
|
||||||
|
bool innerIsImgui = (inner == null);
|
||||||
|
if (innerIsImgui)
|
||||||
|
{
|
||||||
|
var throwaway = new InspectorElement();
|
||||||
|
MethodInfo m = typeof(InspectorElement).GetMethod("CreateIMGUIInspectorFromEditor",
|
||||||
|
BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
inner = m.Invoke(throwaway, new object[] {serializedObject, this, false}) as VisualElement;
|
||||||
|
}
|
||||||
|
|
||||||
_visualElement = new MAVisualElement();
|
_visualElement = new MAVisualElement();
|
||||||
_visualElement.Add(inner);
|
_visualElement.Add(inner);
|
||||||
|
|
||||||
_suppressOnceDefaultMargins = true;
|
_suppressOnceDefaultMargins = innerIsImgui;
|
||||||
return _visualElement;
|
return _visualElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,69 +7,38 @@ using VRC.SDK3.Avatars.ScriptableObjects;
|
|||||||
|
|
||||||
namespace nadena.dev.modular_avatar.core.editor
|
namespace nadena.dev.modular_avatar.core.editor
|
||||||
{
|
{
|
||||||
internal class MenuPreviewGUI
|
internal class MenuObjectHeader
|
||||||
{
|
{
|
||||||
private const float INDENT_PER_LEVEL = 2;
|
private const float INDENT_PER_LEVEL = 2;
|
||||||
private Action _redraw;
|
private static float _indentLevel = 0;
|
||||||
private float _indentLevel = 0;
|
|
||||||
private readonly Dictionary<object, Action> _guiNodes = new Dictionary<object, Action>();
|
|
||||||
|
|
||||||
public MenuPreviewGUI(Action redraw)
|
private UnityEngine.Object _headerObj;
|
||||||
|
private SerializedProperty _disableProp;
|
||||||
|
|
||||||
|
public MenuObjectHeader(UnityEngine.Object headerObj, SerializedProperty disableProp = null)
|
||||||
{
|
{
|
||||||
_redraw = redraw;
|
_headerObj = headerObj;
|
||||||
|
_disableProp = disableProp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DoGUI(MenuSource root)
|
public static void ClearIndent()
|
||||||
{
|
{
|
||||||
_indentLevel = 0;
|
_indentLevel = 0;
|
||||||
new VisitorContext(this).PushNode(root);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DoGUI(ModularAvatarMenuInstaller root)
|
public IDisposable Scope()
|
||||||
{
|
{
|
||||||
_indentLevel = 0;
|
GUILayout.BeginHorizontal();
|
||||||
new VisitorContext(this).PushMenuInstaller(root);
|
GUILayout.Space(_indentLevel);
|
||||||
}
|
_indentLevel += INDENT_PER_LEVEL;
|
||||||
|
|
||||||
public void DoGUI(VRCExpressionsMenu menu, GameObject parameterReference = null)
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||||
{
|
|
||||||
_indentLevel = 0;
|
|
||||||
new VisitorContext(this).PushNode(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PushGuiNode(object key, Func<Action> guiBuilder)
|
if (_headerObj != null)
|
||||||
{
|
|
||||||
if (!_guiNodes.TryGetValue(key, out var gui))
|
|
||||||
{
|
{
|
||||||
gui = guiBuilder();
|
var oldIndent = EditorGUI.indentLevel;
|
||||||
_guiNodes.Add(key, gui);
|
EditorGUI.indentLevel = 0;
|
||||||
}
|
try
|
||||||
|
|
||||||
gui();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Header
|
|
||||||
{
|
|
||||||
private MenuPreviewGUI _gui;
|
|
||||||
private UnityEngine.Object _headerObj;
|
|
||||||
private SerializedProperty _disableProp;
|
|
||||||
|
|
||||||
public Header(MenuPreviewGUI gui, UnityEngine.Object headerObj, SerializedProperty disableProp = null)
|
|
||||||
{
|
|
||||||
_gui = gui;
|
|
||||||
_headerObj = headerObj;
|
|
||||||
_disableProp = disableProp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IDisposable Scope()
|
|
||||||
{
|
|
||||||
GUILayout.BeginHorizontal();
|
|
||||||
GUILayout.Space(_gui._indentLevel);
|
|
||||||
_gui._indentLevel += INDENT_PER_LEVEL;
|
|
||||||
|
|
||||||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
||||||
|
|
||||||
if (_headerObj != null)
|
|
||||||
{
|
{
|
||||||
GUILayout.BeginHorizontal();
|
GUILayout.BeginHorizontal();
|
||||||
using (new EditorGUI.DisabledScope(true))
|
using (new EditorGUI.DisabledScope(true))
|
||||||
@ -91,27 +60,66 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
|
|
||||||
GUILayout.EndHorizontal();
|
GUILayout.EndHorizontal();
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
return new ScopeSentinel(_gui);
|
{
|
||||||
|
EditorGUI.indentLevel = oldIndent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ScopeSentinel : IDisposable
|
return new ScopeSentinel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ScopeSentinel : IDisposable
|
||||||
|
{
|
||||||
|
public ScopeSentinel()
|
||||||
{
|
{
|
||||||
private readonly MenuPreviewGUI _gui;
|
}
|
||||||
|
|
||||||
public ScopeSentinel(MenuPreviewGUI gui)
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_gui = gui;
|
GUILayout.EndVertical();
|
||||||
}
|
_indentLevel -= INDENT_PER_LEVEL;
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
GUILayout.EndVertical();
|
|
||||||
_gui._indentLevel -= INDENT_PER_LEVEL;
|
|
||||||
GUILayout.EndHorizontal();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class MenuPreviewGUI
|
||||||
|
{
|
||||||
|
private Action _redraw;
|
||||||
|
private readonly Dictionary<object, Action> _guiNodes = new Dictionary<object, Action>();
|
||||||
|
|
||||||
|
public MenuPreviewGUI(Action redraw)
|
||||||
|
{
|
||||||
|
_redraw = redraw;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DoGUI(MenuSource root)
|
||||||
|
{
|
||||||
|
new VisitorContext(this).PushNode(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DoGUI(ModularAvatarMenuInstaller root)
|
||||||
|
{
|
||||||
|
new VisitorContext(this).PushMenuInstaller(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DoGUI(VRCExpressionsMenu menu, GameObject parameterReference = null)
|
||||||
|
{
|
||||||
|
new VisitorContext(this).PushNode(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PushGuiNode(object key, Func<Action> guiBuilder)
|
||||||
|
{
|
||||||
|
if (!_guiNodes.TryGetValue(key, out var gui))
|
||||||
|
{
|
||||||
|
gui = guiBuilder();
|
||||||
|
_guiNodes.Add(key, gui);
|
||||||
|
}
|
||||||
|
|
||||||
|
gui();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private class VisitorContext : NodeContext
|
private class VisitorContext : NodeContext
|
||||||
{
|
{
|
||||||
@ -127,7 +135,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
{
|
{
|
||||||
_gui.PushGuiNode((expMenu, parameterReference), () =>
|
_gui.PushGuiNode((expMenu, parameterReference), () =>
|
||||||
{
|
{
|
||||||
var header = new Header(_gui, expMenu);
|
var header = new MenuObjectHeader(expMenu);
|
||||||
var obj = new SerializedObject(expMenu);
|
var obj = new SerializedObject(expMenu);
|
||||||
var controls = obj.FindProperty(nameof(expMenu.controls));
|
var controls = obj.FindProperty(nameof(expMenu.controls));
|
||||||
var subGui = new List<MenuItemCoreGUI>();
|
var subGui = new List<MenuItemCoreGUI>();
|
||||||
@ -143,7 +151,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
{
|
{
|
||||||
foreach (var gui in subGui)
|
foreach (var gui in subGui)
|
||||||
{
|
{
|
||||||
using (new Header(_gui, null).Scope())
|
using (new MenuObjectHeader(null).Scope())
|
||||||
{
|
{
|
||||||
gui.DoGUI();
|
gui.DoGUI();
|
||||||
}
|
}
|
||||||
@ -164,7 +172,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
{
|
{
|
||||||
_gui.PushGuiNode(item, () =>
|
_gui.PushGuiNode(item, () =>
|
||||||
{
|
{
|
||||||
var header = new Header(_gui, item,
|
var header = new MenuObjectHeader(item,
|
||||||
new SerializedObject(item.gameObject).FindProperty("m_IsActive"));
|
new SerializedObject(item.gameObject).FindProperty("m_IsActive"));
|
||||||
var gui = new MenuItemCoreGUI(new SerializedObject(item), _gui._redraw);
|
var gui = new MenuItemCoreGUI(new SerializedObject(item), _gui._redraw);
|
||||||
return () =>
|
return () =>
|
||||||
@ -178,7 +186,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
using (new Header(_gui, source as UnityEngine.Object).Scope())
|
using (new MenuObjectHeader(source as UnityEngine.Object).Scope())
|
||||||
{
|
{
|
||||||
if (_visited.Contains(source)) return;
|
if (_visited.Contains(source)) return;
|
||||||
_visited.Add(source);
|
_visited.Add(source);
|
||||||
@ -190,7 +198,7 @@ namespace nadena.dev.modular_avatar.core.editor
|
|||||||
|
|
||||||
public void PushNode(ModularAvatarMenuInstaller installer)
|
public void PushNode(ModularAvatarMenuInstaller installer)
|
||||||
{
|
{
|
||||||
using (new Header(_gui, installer).Scope())
|
using (new MenuObjectHeader(installer).Scope())
|
||||||
{
|
{
|
||||||
PushMenuInstaller(installer);
|
PushMenuInstaller(installer);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,108 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.UIElements;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UIElements;
|
||||||
|
|
||||||
|
namespace nadena.dev.modular_avatar.core.editor
|
||||||
|
{
|
||||||
|
[CustomEditor(typeof(ToggleGroup))]
|
||||||
|
internal class ToggleGroupInspector : MAEditorBase
|
||||||
|
{
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
EditorApplication.hierarchyChanged += Invalidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDisable()
|
||||||
|
{
|
||||||
|
EditorApplication.hierarchyChanged -= Invalidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Action> _menuItemActions = null;
|
||||||
|
|
||||||
|
private void Invalidate()
|
||||||
|
{
|
||||||
|
var target = (ToggleGroup) this.target;
|
||||||
|
var avatar = RuntimeUtil.FindAvatarInParents(target.transform);
|
||||||
|
var menuItems = avatar.GetComponentsInChildren<ModularAvatarMenuItem>(true);
|
||||||
|
|
||||||
|
_menuItemActions = new List<Action>();
|
||||||
|
foreach (var menuItem in menuItems.Where(item => item.toggleGroup == target))
|
||||||
|
{
|
||||||
|
var node = CreateMenuItemNode(menuItem);
|
||||||
|
_menuItemActions.Add(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Action CreateMenuItemNode(ModularAvatarMenuItem menuItem)
|
||||||
|
{
|
||||||
|
bool foldout = false;
|
||||||
|
|
||||||
|
var coreUI = new MenuItemCoreGUI(new SerializedObject(menuItem), Repaint);
|
||||||
|
var enableMenuItem = new SerializedObject(menuItem.gameObject).FindProperty("m_IsActive");
|
||||||
|
|
||||||
|
List<Action> foldoutInspectors = null;
|
||||||
|
|
||||||
|
return () =>
|
||||||
|
{
|
||||||
|
using (new MenuObjectHeader(menuItem, enableMenuItem).Scope())
|
||||||
|
{
|
||||||
|
coreUI.DoGUI();
|
||||||
|
|
||||||
|
foldout = EditorGUILayout.Foldout(foldout, "Actions");
|
||||||
|
if (foldout)
|
||||||
|
{
|
||||||
|
if (foldoutInspectors == null)
|
||||||
|
{
|
||||||
|
foldoutInspectors = menuItem.GetComponents<MenuAction>()
|
||||||
|
.Select(action =>
|
||||||
|
{
|
||||||
|
var component = (Component) action;
|
||||||
|
var editor = CreateEditor(component);
|
||||||
|
var enabled_prop = new SerializedObject(component).FindProperty("m_Enabled");
|
||||||
|
|
||||||
|
return (Action) (() =>
|
||||||
|
{
|
||||||
|
using (new MenuObjectHeader(component, enabled_prop).Scope())
|
||||||
|
{
|
||||||
|
editor.OnInspectorGUI();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var inspector in foldoutInspectors)
|
||||||
|
{
|
||||||
|
inspector();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnInnerInspectorGUI()
|
||||||
|
{
|
||||||
|
if (_menuItemActions == null) Invalidate();
|
||||||
|
|
||||||
|
EditorGUILayout.LabelField("Bound menu items", EditorStyles.boldLabel);
|
||||||
|
foreach (var action in _menuItemActions)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
EditorGUI.indentLevel++;
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
EditorGUI.indentLevel--;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.Space(4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 357527439f084cd6812d6e5dcd9692f8
|
||||||
|
timeCreated: 1677595893
|
Loading…
x
Reference in New Issue
Block a user