// TabbedInspector.cs using System; using System.Collections.Generic; using UnityEditor; using UnityEngine; namespace UnityEditor.Utils { /// /// 复用的 tab 管理器(非 Editor),供任意 Editor 组合使用。 /// 用法:在 Editor 的 OnEnable 创建一个实例并 RegisterTab / AppendToTab, /// 在 OnInspectorGUI 调用 DrawTabs()。 /// public class TabbedInspector { public class Tab { public string title; public string iconName; public List callbacks = new List(); public Tab(string t, string icon) { title = t; iconName = icon; } } List _tabs = new List(); int _currentTabIndex = 0; string _prefsKey; // 用于保存每类 inspector 的选择(可选) public TabbedInspector(string prefsKey = null) { _prefsKey = prefsKey; if (!string.IsNullOrEmpty(_prefsKey)) { _currentTabIndex = EditorPrefs.GetInt(_prefsKey, 0); } } public void RegisterTab(string title, string iconName, Action drawCallback) { if (string.IsNullOrEmpty(title)) return; var tab = _tabs.Find(t => t.title == title); if (tab == null) { tab = new Tab(title, iconName); _tabs.Add(tab); } else { tab.iconName = iconName; tab.callbacks.Clear(); } if (drawCallback != null) tab.callbacks.Add(drawCallback); } public void AppendToTab(string title, Action drawCallback, bool last = true) { if (string.IsNullOrEmpty(title) || drawCallback == null) return; var tab = _tabs.Find(t => t.title == title); if (tab == null) { tab = new Tab(title, "d_DefaultAsset Icon"); _tabs.Add(tab); } if (!tab.callbacks.Contains(drawCallback)) { if (last) tab.callbacks.Add(drawCallback); else tab.callbacks.Insert(0, drawCallback); } } public void RemoveCallbackFromTab(string title, Action drawCallback) { if (string.IsNullOrEmpty(title) || drawCallback == null) return; var tab = _tabs.Find(t => t.title == title); if (tab == null) return; tab.callbacks.RemoveAll(cb => cb == drawCallback); } public void UnregisterTab(string title) { if (string.IsNullOrEmpty(title)) return; _tabs.RemoveAll(t => t.title == title); if (_tabs.Count == 0) _currentTabIndex = 0; else if (_currentTabIndex >= _tabs.Count) _currentTabIndex = Mathf.Max(0, _tabs.Count - 1); } public void EnsureDefaultTab(string title, string iconName, Action defaultCallback) { var tab = _tabs.Find(t => t.title == title); if (tab == null) { tab = new Tab(title, iconName); _tabs.Insert(0, tab); } if (!tab.callbacks.Contains(defaultCallback)) tab.callbacks.Insert(0, defaultCallback); } public void DrawTabs() { if (_tabs == null || _tabs.Count == 0) { // nothing to draw return; } EditorGUILayout.BeginHorizontal(); for (int i = 0; i < _tabs.Count; i++) { var tab = _tabs[i]; bool isActive = (i == _currentTabIndex); var style = new GUIStyle(EditorStyles.toolbarButton) { fixedHeight = 25, fontSize = 11, fontStyle = isActive ? FontStyle.Bold : FontStyle.Normal }; var icon = EditorGUIUtility.IconContent(tab.iconName)?.image; var content = new GUIContent(icon, tab.title); if (GUILayout.Button(content, style)) { _currentTabIndex = i; SaveIndex(); } if (isActive) { // 一条下划线表示选中 Rect rect = GUILayoutUtility.GetLastRect(); EditorGUI.DrawRect(new Rect(rect.x, rect.yMax - 2, rect.width, 2), new Color(0.1f, 0.5f, 0.9f)); } } EditorGUILayout.EndHorizontal(); GUILayout.Space(4); // draw callbacks for current tab var callbacks = _tabs[_currentTabIndex].callbacks; if (callbacks != null) { foreach (var cb in callbacks) { try { cb?.Invoke(); } catch (ExitGUIException) { throw; } catch (Exception e) { Debug.LogException(e); } } } } void SaveIndex() { if (!string.IsNullOrEmpty(_prefsKey)) EditorPrefs.SetInt(_prefsKey, _currentTabIndex); } } }