using UnityEditor; using UnityEngine; using UnityEngine.UIElements; using System.Linq; using System; using System.IO; using System.Collections.Generic; using UnityEditor.SceneManagement; using UXScroller = UnityEngine.UIElements.Scroller; namespace AlicizaX.UXTool { public static class PrefabTabs { public static readonly int m_maxCharacters = 20; public static readonly int m_minCharacters = 13; public static readonly int m_maxWidth = 150; public static readonly int m_minWidth = 100; public static VisualElement prefabTabsPanel; private static ScrollView TabsList; private static List m_tabs; private static string m_selectedTab; private static SceneView sceneView = SceneView.lastActiveSceneView; private static int width; public static string SelectedGuid { get { return m_selectedTab; } set { m_selectedTab = value; EditorPrefs.SetString("AlicizaUXTool_SelectedTab", value); // 记录当前选中的Tab } } [InitializeOnLoadMethod] public static void Initialize() { UXDesinUtil.OnEnterDesignMode += RegisterEvents; UXDesinUtil.OnExitDesignMode += UnRegisterEvents; if (UXDesinUtil.InDesign) { RegisterEvents(); } else { UnRegisterEvents(); } } private static void RegisterEvents() { PrefabStageUtils.OnEditRootReplaced += OnEditRootReplaced; EditorApplication.delayCall += OnEditorLoaded; UXPrefabTabsConfig.Instance.SyncTabs(); } private static void UnRegisterEvents() { PrefabStageUtils.OnEditRootReplaced -= OnEditRootReplaced; EditorApplication.delayCall -= OnEditorLoaded; } private static void OnEditorLoaded() { if (UXDesinUtil.InDesign) { CreateGUI(); RefreshTabs(false, 0); OpenOrFallbackToLastValidTab(); } } private static void OnEditRootReplaced(string assetPath) { string guid = AssetDatabase.AssetPathToGUID(assetPath); SelectedGuid = guid; RefreshTabs(false, 0); } private static void CreateGUI() { sceneView = SceneView.lastActiveSceneView; if (sceneView == null) return; prefabTabsPanel = new VisualElement(); prefabTabsPanel.name = "Tabs"; prefabTabsPanel.style.height = new StyleLength(new Length(100, LengthUnit.Percent)); prefabTabsPanel.style.position = Position.Relative; prefabTabsPanel.style.top = 0; prefabTabsPanel.style.right = 0; prefabTabsPanel.style.left = 0; prefabTabsPanel.style.backgroundColor = new Color(48f / 255f, 48f / 255f, 48f / 255f); // rgb(48, 48, 48) prefabTabsPanel.style.flexGrow = 0; prefabTabsPanel.style.paddingRight = 0; prefabTabsPanel.style.marginRight = 160; TabsList = new ScrollView(ScrollViewMode.Horizontal); TabsList.name = "TabsList"; TabsList.horizontalScrollerVisibility = ScrollerVisibility.AlwaysVisible; TabsList.verticalScrollerVisibility = ScrollerVisibility.Hidden; prefabTabsPanel.Add(TabsList); prefabTabsPanel.style.flexGrow = 1; prefabTabsPanel.style.height = Length.Percent(50); prefabTabsPanel.contentContainer.RegisterCallback((e) => { Vector2 old = TabsList.scrollOffset; TabsList.scrollOffset = old; width = m_tabs.Count == 0 ? m_minWidth : Math.Min(Math.Max(m_minWidth, (int)sceneView.position.width / m_tabs.Count), m_maxWidth); RefreshTabs(false, 0); }); UXScroller scroller = TabsList.horizontalScroller; scroller.Remove(scroller.lowButton); scroller.Remove(scroller.highButton); scroller.slider.style.height = 4; scroller.slider.style.marginBottom = 0; scroller.slider.style.paddingBottom = 0; scroller.slider.style.marginLeft = scroller.slider.style.marginRight = 0; scroller.style.height = 5; scroller.style.bottom = 0; scroller.style.marginBottom = 0; var parent = sceneView.rootVisualElement.parent.parent.parent.parent.parent.Q(); if (parent != null) parent.Add(prefabTabsPanel); parent.style.flexShrink = 1; parent.style.width = Length.Percent(100); parent.style.flexGrow = 0.08f; parent.style.height = Length.Percent(5); m_tabs = UXPrefabTabsConfig.Instance.tabs; m_selectedTab = EditorPrefs.GetString("AlicizaUXTool_SelectedTab", string.Empty); if (m_tabs.Find(p => p == m_selectedTab) == null) m_selectedTab = string.Empty; RefreshTabs(false, 0); // 初始化时刷新 Tabs } public static void RefreshTabs(bool isclose, int width) { if (prefabTabsPanel == null) return; #if UNITY_2021_3_OR_NEWER prefabTabsPanel.style.top = 0; #else prefabTabsPanel.style.top = string.IsNullOrEmpty(m_selectedTab) ? 21 : 46; #endif bool flag = TabsList.contentContainer.childCount == m_tabs.Count; TabsList.Clear(); Button selectButton = null; foreach (var item in m_tabs) { string path = AssetDatabase.GUIDToAssetPath(item); if (path != "" && File.Exists(path)) { var tab = new PrefabSingleTab(new FileInfo(path), item, m_tabs.Count, isclose, width); if (item == m_selectedTab) { tab.visual.style.backgroundColor = new Color(95f / 255, 95f / 255, 95f / 255, 1); selectButton = tab.visual; } else { tab.visual.style.backgroundColor = new Color(60f / 255, 60f / 255, 60f / 255, 1); } TabsList.Add(tab.visual); } } if (selectButton != null) { if (flag) { selectButton.RegisterCallback(FirstLayoutCallback, selectButton); } else { TabsList.contentContainer.RegisterCallback(FirstLayoutCallback, selectButton); } } UXPrefabTabsConfig.Instance.tabs = m_tabs; // 保存当前 Tabs 状态 UXPrefabTabsConfig.Save(); // 保存到磁盘 } private static void OpenOrFallbackToLastValidTab() { // 尝试打开当前选中的GUID if (!string.IsNullOrEmpty(m_selectedTab) && m_tabs.Contains(m_selectedTab)) { PrefabStageUtils.SwitchStage(AssetDatabase.GUIDToAssetPath(m_selectedTab)); } else { // 移除不存在的GUID并选中最后一个有效的 m_tabs.RemoveAll(guid => { string path = AssetDatabase.GUIDToAssetPath(guid); return string.IsNullOrEmpty(path) || !File.Exists(path); }); if (m_tabs.Count > 0) { m_selectedTab = m_tabs.Last(); PrefabStageUtils.SwitchStage(AssetDatabase.GUIDToAssetPath(m_selectedTab)); } else { m_selectedTab = string.Empty; PrefabStageUtils.OpenDefaultStage(); } } RefreshTabs(false, 0); } public static void OpenTab(string guid, bool replaceRoot) { SelectedGuid = guid; if (replaceRoot) { PrefabStageUtils.SwitchStage(AssetDatabase.GUIDToAssetPath(guid)); } if (!m_tabs.Contains(guid)) { m_tabs.Add(guid); width = Math.Min(Math.Max(m_minWidth, (int)sceneView.position.width / m_tabs.Count), m_maxWidth); } RefreshTabs(false, 0); } public static void CloseTab(string guid) { m_tabs.Remove(guid); OpenOrFallbackToLastValidTab(); } private static void FirstLayoutCallback(GeometryChangedEvent evt, VisualElement v) { if (!TabsList.Contains(v)) return; TabsList.ScrollTo(v); } } }