using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using nadena.dev.modular_avatar.ui; using nadena.dev.ndmf.localization; using nadena.dev.ndmf.preview; using UnityEditor; using UnityEditor.UIElements; using UnityEngine; using UnityEngine.UIElements; namespace nadena.dev.modular_avatar.core.editor.Simulator { internal class ROSimulator : EditorWindow, IHasCustomMenu { public static PublishedValue> PropertyOverrides = new(null); public static PublishedValue> MenuItemOverrides = new(null); internal static string ROOT_PATH = "Packages/nadena.dev.modular-avatar/Editor/ReactiveObjects/Simulator/"; private static string USS = ROOT_PATH + "ROSimulator.uss"; private static string UXML = ROOT_PATH + "ROSimulator.uxml"; private static string EFFECT_GROUP_UXML = ROOT_PATH + "EffectGroup.uxml"; private ObjectField f_inspecting; private VisualElement e_debugInfo; private VisualTreeAsset effectGroupTemplate; private StyleSheet uss; [MenuItem(UnityMenuItems.GameObject_ShowReactionDebugger, false, UnityMenuItems.GameObject_ShowReactionDebuggerOrder)] internal static void ShowWindow(MenuCommand command) { OpenDebugger(command.context as GameObject); } private void Awake() { void SetTitle(EditorWindow w) { w.titleContent = new GUIContent(Localization.L.GetLocalizedString("ro_sim.window.title")); } LanguagePrefs.RegisterLanguageChangeCallback(this, SetTitle); SetTitle(this); } public static void OpenDebugger(GameObject target) { var window = GetWindow(); if (window.is_enabled && window.locked) return; window.locked = target != Selection.activeGameObject; // avoid racing with initial creation if (window.f_inspecting == null) { window.LoadUI(); } // ReSharper disable once PossibleNullReferenceException window.f_inspecting.SetValueWithoutNotify(target); window.RefreshUI(); } private void OnEnable() { PropertyOverrides.Value = ImmutableDictionary.Empty; MenuItemOverrides.Value = ImmutableDictionary.Empty; EditorApplication.delayCall += LoadUI; Selection.selectionChanged += SelectionChanged; is_enabled = true; } private void OnDisable() { Selection.selectionChanged -= SelectionChanged; is_enabled = false; // Delay this to ensure that we don't try to change this value from within assembly reload callbacks // (which generates a noisy exception) EditorApplication.delayCall += () => { PropertyOverrides.Value = null; MenuItemOverrides.Value = null; }; } private ComputeContext _lastComputeContext; private GameObject currentSelection; private GUIStyle lockButtonStyle; private bool locked, is_enabled; private Dictionary<(int, string), bool> foldoutState = new(); private Button _btn_clear; private bool _refreshPending; private void RequestRefresh() { if (_refreshPending) return; _refreshPending = true; EditorApplication.delayCall += RefreshUI; } private void UpdatePropertyOverride(string prop, bool? enable, float f_val) { if (enable == null) { PropertyOverrides.Value = PropertyOverrides.Value.Remove(prop); } else if (enable.Value) { PropertyOverrides.Value = PropertyOverrides.Value.SetItem(prop, f_val); } else { PropertyOverrides.Value = PropertyOverrides.Value.SetItem(prop, 0f); } RequestRefresh(); } private void UpdateMenuItemOverride(string prop, ModularAvatarMenuItem item, bool? value) { if (value == null) { MenuItemOverrides.Value = MenuItemOverrides.Value.Remove(prop); } else if (value.Value) { MenuItemOverrides.Value = MenuItemOverrides.Value.SetItem(prop, item); } else { if (MenuItemOverrides.Value.TryGetValue(prop, out var existing) && ReferenceEquals(existing, item)) MenuItemOverrides.Value = MenuItemOverrides.Value.SetItem(prop, null); } RequestRefresh(); } private void ShowButton(Rect rect) { if (lockButtonStyle == null) { lockButtonStyle = "IN LockButton"; } locked = GUI.Toggle(rect, locked, GUIContent.none, lockButtonStyle); } void IHasCustomMenu.AddItemsToMenu(GenericMenu menu) { menu.AddItem(new GUIContent("Lock"), locked, () => locked = !locked); } void SelectionChanged() { if (locked) return; if (currentSelection != Selection.activeGameObject) { UpdateSelection(); } } private void LoadUI() { var root = rootVisualElement; root.Clear(); root.AddToClassList("rootVisualContent"); uss = AssetDatabase.LoadAssetAtPath(USS); root.styleSheets.Add(uss); var content = AssetDatabase.LoadAssetAtPath(UXML).CloneTree(); root.Add(content); Localization.L.LocalizeUIElements(content); root.Q