From a49d2d74e68c57bd486bae5e4ae51ff9847ee1fa Mon Sep 17 00:00:00 2001 From: Lillith Rose Date: Wed, 7 Jan 2026 16:43:34 -0500 Subject: [PATCH] feat(menustyling) remove other styling before applying --- MenuStyling/Editor/MenuStylingPlugin.cs | 64 +++++++++++++++++++------ MenuStyling/Runtime/MenuStyling.cs | 1 + 2 files changed, 51 insertions(+), 14 deletions(-) diff --git a/MenuStyling/Editor/MenuStylingPlugin.cs b/MenuStyling/Editor/MenuStylingPlugin.cs index 86beea1..5805d0d 100644 --- a/MenuStyling/Editor/MenuStylingPlugin.cs +++ b/MenuStyling/Editor/MenuStylingPlugin.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Text.RegularExpressions; using gay.lilyy.MenuStyling; using nadena.dev.ndmf; using nadena.dev.ndmf.vrchat; @@ -22,17 +23,26 @@ namespace gay.lilyy.MenuStyling if (obj != null) { var descriptor = ctx.VRChatAvatarDescriptor(); - descriptor.expressionsMenu = DuplicateMenu(descriptor.expressionsMenu); - WalkMenu(obj, descriptor.expressionsMenu); + var processedMenus = new HashSet(); + descriptor.expressionsMenu = DuplicateMenu(descriptor.expressionsMenu, processedMenus); + var walkProcessedMenus = new HashSet(); + WalkMenu(obj, descriptor.expressionsMenu, walkProcessedMenus); Object.DestroyImmediate(obj); } }); } - private VRCExpressionsMenu DuplicateMenu(VRCExpressionsMenu menu) + private VRCExpressionsMenu DuplicateMenu(VRCExpressionsMenu menu, HashSet processedMenus) { if (menu == null) return null; + // Prevent circular references - if we've already processed this menu, return null to break the cycle + if (processedMenus.Contains(menu)) + { + return null; + } + processedMenus.Add(menu); + var newMenu = ScriptableObject.CreateInstance(); newMenu.controls = new List(); @@ -44,7 +54,7 @@ namespace gay.lilyy.MenuStyling type = control.type, icon = control.icon, parameter = control.parameter, - subMenu = DuplicateMenu(control.subMenu), + subMenu = DuplicateMenu(control.subMenu, processedMenus), value = control.value, style = control.style, labels = control.labels, @@ -56,24 +66,50 @@ namespace gay.lilyy.MenuStyling return newMenu; } - private void WalkMenu(MenuStylingConfig config, VRCExpressionsMenu menu) + private string StripTextMeshProTags(string text) { + if (string.IsNullOrEmpty(text)) return text; + + // Remove all TextMeshPro/rich text tags + // Pattern matches: , , , , + text = Regex.Replace(text, @"<[^>]+>", ""); + + return text; + } + + private void WalkMenu(MenuStylingConfig config, VRCExpressionsMenu menu, HashSet processedMenus) + { + if (menu == null) return; + + // Prevent circular references + if (processedMenus.Contains(menu)) + { + return; + } + processedMenus.Add(menu); + foreach (var child in menu.controls) { if (child.name != " " && child.name != "" && child.name != null) { - if (!child.name.StartsWith(config.Prefix)) - { - child.name = config.Prefix + child.name; - } - if (!child.name.EndsWith(config.Suffix)) - { - child.name += config.Suffix; - } + // Strip all TextMeshPro styling tags first if enabled + if (config.RemovePreexistingStyling) + { + child.name = StripTextMeshProTags(child.name); + } + + if (!child.name.StartsWith(config.Prefix)) + { + child.name = config.Prefix + child.name; + } + if (!child.name.EndsWith(config.Suffix)) + { + child.name += config.Suffix; + } } if (child.type == Control.ControlType.SubMenu && child.subMenu != null) { - WalkMenu(config, child.subMenu); + WalkMenu(config, child.subMenu, processedMenus); } } } diff --git a/MenuStyling/Runtime/MenuStyling.cs b/MenuStyling/Runtime/MenuStyling.cs index 7e4e336..617835a 100644 --- a/MenuStyling/Runtime/MenuStyling.cs +++ b/MenuStyling/Runtime/MenuStyling.cs @@ -8,5 +8,6 @@ namespace gay.lilyy.MenuStyling { public string Prefix = ""; public string Suffix = ""; + public bool RemovePreexistingStyling = true; } } \ No newline at end of file