diff --git a/Editor/Inspector/Menu/MenuItemGUI.cs b/Editor/Inspector/Menu/MenuItemGUI.cs index 6b27a58a..bf3cfccd 100644 --- a/Editor/Inspector/Menu/MenuItemGUI.cs +++ b/Editor/Inspector/Menu/MenuItemGUI.cs @@ -59,6 +59,8 @@ namespace nadena.dev.modular_avatar.core.editor internal class MenuItemCoreGUI { + private const string ImpliesRichText = "<"; + private static readonly ObjectIDGenerator IdGenerator = new ObjectIDGenerator(); private readonly GameObject _parameterReference; private readonly Action _redraw; @@ -92,11 +94,14 @@ namespace nadena.dev.modular_avatar.core.editor private readonly SerializedProperty _prop_isDefault; private readonly SerializedProperty _prop_automaticValue; + private readonly SerializedProperty _prop_label; + public bool AlwaysExpandContents = false; public bool ExpandContents = false; private readonly Dictionary _knownParameters = new(); private bool _parameterSourceNotDetermined; + private bool _useLabel; public MenuItemCoreGUI(SerializedObject obj, Action redraw) { @@ -144,6 +149,8 @@ namespace nadena.dev.modular_avatar.core.editor _prop_isDefault = obj.FindProperty(nameof(ModularAvatarMenuItem.isDefault)); _prop_automaticValue = obj.FindProperty(nameof(ModularAvatarMenuItem.automaticValue)); + _prop_label = obj.FindProperty(nameof(ModularAvatarMenuItem.label)); + _previewGUI = new MenuPreviewGUI(redraw); } @@ -260,11 +267,69 @@ namespace nadena.dev.modular_avatar.core.editor EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginVertical(); - EditorGUI.BeginChangeCheck(); - EditorGUILayout.PropertyField(_name, G("menuitem.prop.name")); - if (EditorGUI.EndChangeCheck()) + + EditorGUILayout.BeginHorizontal(); + + if (_parameterReference == null) { - _name.serializedObject.ApplyModifiedProperties(); + EditorGUI.BeginChangeCheck(); + if (_obj != null && _obj.isEditingMultipleObjects) + { + EditorGUILayout.PropertyField(_prop_label, G("menuitem.prop.name")); + } + else + { + EditorGUILayout.PropertyField(_name, G("menuitem.prop.name")); + } + if (EditorGUI.EndChangeCheck()) + { + _name.serializedObject.ApplyModifiedProperties(); + } + } + else + { + _useLabel |= !string.IsNullOrEmpty(_prop_label.stringValue); + + if (!_useLabel) + { + EditorGUI.BeginChangeCheck(); + var previousName = _name.stringValue; + EditorGUILayout.PropertyField(_name, G("menuitem.prop.name")); + if (EditorGUI.EndChangeCheck()) + { + if (!previousName.Contains(ImpliesRichText) && _name.stringValue.Contains(ImpliesRichText)) + { + _prop_label.stringValue = _name.stringValue; + } + else + { + _name.serializedObject.ApplyModifiedProperties(); + } + } + } + else + { + EditorGUILayout.PropertyField(_prop_label, G("menuitem.prop.name")); + } + + var linkIcon = EditorGUIUtility.IconContent(_useLabel ? "UnLinked" : "Linked").image; + var guiIcon = new GUIContent(linkIcon, S(_useLabel ? "menuitem.label.gameobject_name.tooltip" : "menuitem.label.long_name.tooltip")); + if (GUILayout.Button(guiIcon, GUILayout.Height(EditorGUIUtility.singleLineHeight), GUILayout.Width(25))) + { + _prop_label.stringValue = !_useLabel ? _name.stringValue : ""; + _useLabel = !_useLabel; + } + } + + EditorGUILayout.EndHorizontal(); + + if (_useLabel && _prop_label.stringValue.Contains(ImpliesRichText)) + { + var style = new GUIStyle(EditorStyles.textField); + style.richText = true; + style.alignment = TextAnchor.MiddleCenter; + + EditorGUILayout.LabelField(" ", _prop_label.stringValue, style, GUILayout.Height(EditorGUIUtility.singleLineHeight * 3)); } EditorGUILayout.PropertyField(_texture, G("menuitem.prop.icon")); @@ -274,7 +339,7 @@ namespace nadena.dev.modular_avatar.core.editor _parameterGUI.DoGUI(true); ShowInnateParameterGUI(); - + EditorGUILayout.EndVertical(); if (_texture != null) diff --git a/Editor/Localization/en-US.json b/Editor/Localization/en-US.json index 7609d0d0..e01648df 100644 --- a/Editor/Localization/en-US.json +++ b/Editor/Localization/en-US.json @@ -286,6 +286,10 @@ "ro_sim.effect_group.rule_inverted": "This rule is inverted", "ro_sim.effect_group.rule_inverted.tooltip": "This rule will be applied when one of its conditions is NOT met", "ro_sim.effect_group.conditions": "Conditions", + + "menuitem.label.long_name.tooltip": "Use a long name which may contain rich text and line breaks.", + "menuitem.label.gameobject_name.tooltip": "Use the GameObject name.", + "remove-vertex-color.mode": "Mode", "remove-vertex-color.mode.Remove": "Remove Vertex Colors", "remove-vertex-color.mode.DontRemove": "Keep Vertex Colors", @@ -296,4 +300,5 @@ "sync-param-sequence.parameters.tooltip": "The asset to store common parameters in. Do not use the same Expression Parameters that you have set in your avatar descriptor.", "sync-param-sequence.create-asset": "New", "sync-param-sequence.create-asset.tooltip": "Creates a new expression parameters asset" + } diff --git a/Runtime/ModularAvatarMenuItem.cs b/Runtime/ModularAvatarMenuItem.cs index b5fd5d31..d8cf2d36 100644 --- a/Runtime/ModularAvatarMenuItem.cs +++ b/Runtime/ModularAvatarMenuItem.cs @@ -49,6 +49,14 @@ namespace nadena.dev.modular_avatar.core /// public bool automaticValue; + /// + /// Although unspecified, the label of a menu may contain rich text and line breaks. + /// If label is not an empty string, this MenuItem will use that as its name. + /// Otherwise, it will use the name of the containing game object as the label. + /// + [Multiline] + public string label = ""; + private void Reset() { // Init settings only when added or reset manually from the Inspector. @@ -98,7 +106,7 @@ namespace nadena.dev.modular_avatar.core var cloned = new VirtualControl(Control); cloned.subMenu = null; - cloned.name = gameObject.name; + cloned.name = string.IsNullOrEmpty(label) ? gameObject.name : label; FilterSubParameters(cloned);