using System; using UnityEditor; using UnityEditor.UIElements; using UnityEngine; using UnityEngine.UIElements; using BaseGames.Equipment; namespace BaseGames.Editor.Modules { /// /// DataHub 护符模块 —— Tab 切换管理 CharmCatalogSO(目录)和 CharmSO(护符)资产。 /// public class CharmModule : IDataModule, IDataModuleOrdered { private const string CharmFolder = "Assets/_Game/Data/Progression/Charms"; private const string CatalogPrefix = "CHM_Catalog"; private const string CharmPrefix = "CHM_"; public string ModuleId => "charm"; public string DisplayName => "护符"; public string IconName => null; public int DisplayOrder => 60; private int _activeTab = 0; // 0 = 目录, 1 = 护符 private SoListPane _catalogPane; private SoListPane _charmPane; private Action _onSelected; private DetailHeader _header; private CharmCatalogSO _selectedCatalog; private CharmSO _selectedCharm; public void Initialize() { _catalogPane = new SoListPane(CharmFolder, CatalogPrefix); _catalogPane.SelectionChanged = s => { _selectedCatalog = s; _onSelected?.Invoke(s); }; _charmPane = new SoListPane(CharmFolder, CharmPrefix, c => $"{c.notchCost}格"); _charmPane.SelectionChanged = s => { _selectedCharm = s; _onSelected?.Invoke(s); }; } public void BuildListPane(VisualElement container, Action onSelected) { _onSelected = onSelected; container.style.flexDirection = FlexDirection.Column; // Tab bar var tabBar = new VisualElement(); tabBar.style.flexDirection = FlexDirection.Row; tabBar.style.borderBottomWidth = 1; tabBar.style.borderBottomColor = new StyleColor(new Color(0.5f, 0.5f, 0.5f, 0.3f)); container.Add(tabBar); var btnCatalog = BuildTabBtn("目录", 0, tabBar); var btnCharms = BuildTabBtn("护符", 1, tabBar); var listArea = new VisualElement(); listArea.style.flexGrow = 1; container.Add(listArea); var tabs = new[] { btnCatalog, btnCharms }; ShowTab(0, listArea, tabs); btnCatalog.clicked += () => ShowTab(0, listArea, tabs); btnCharms.clicked += () => ShowTab(1, listArea, tabs); _catalogPane.Refresh(); _charmPane.Refresh(); } public void BuildDetailPane(VisualElement container, UnityEngine.Object selected) { _header = new DetailHeader(); _header.SetAsset(selected); _header.RenameRequested += name => OnRenameRequested(selected, name); container.Add(_header); if (selected == null) return; if (selected is CharmCatalogSO catalog) { container.Add(BuildCatalogActionBar(catalog)); container.Add(SkillModule.MakeDivider()); container.Add(new InspectorElement(catalog)); } else if (selected is CharmSO charm) { container.Add(BuildCharmCard(charm)); container.Add(BuildCharmActionBar(charm)); container.Add(SkillModule.MakeDivider()); container.Add(new InspectorElement(charm)); } } public void OnActivated() { _catalogPane?.Refresh(); _charmPane?.Refresh(); } // ── Tab UI ──────────────────────────────────────────────────────────── private Button BuildTabBtn(string text, int tabIdx, VisualElement bar) { var btn = new Button { text = text }; btn.style.flexGrow = 1; btn.style.paddingTop = 5; btn.style.paddingBottom = 5; btn.style.borderTopLeftRadius = 0; btn.style.borderTopRightRadius = 0; btn.style.borderBottomLeftRadius = 0; btn.style.borderBottomRightRadius = 0; btn.style.borderLeftWidth = 0; btn.style.borderRightWidth = 0; btn.style.borderTopWidth = 0; btn.style.borderBottomWidth = 0; btn.style.backgroundColor = new StyleColor(Color.clear); btn.userData = tabIdx; bar.Add(btn); return btn; } private void ShowTab(int tab, VisualElement area, Button[] tabBtns) { _activeTab = tab; area.Clear(); for (int i = 0; i < tabBtns.Length; i++) { if (i == tab) { tabBtns[i].style.borderBottomWidth = 2; tabBtns[i].style.borderBottomColor = new StyleColor(new Color(0.4f, 0.65f, 1f, 1f)); tabBtns[i].style.opacity = 1f; } else { tabBtns[i].style.borderBottomWidth = 0; tabBtns[i].style.opacity = 0.65f; } } if (tab == 0) { _catalogPane.style.flexGrow = 1; area.Add(_catalogPane); } else { _charmPane.style.flexGrow = 1; area.Add(_charmPane); } } // ── 重命名 ──────────────────────────────────────────────────────────── private void OnRenameRequested(UnityEngine.Object asset, string newName) { var (ok, err) = AssetOperations.Rename(asset, newName); if (!ok) EditorUtility.DisplayDialog("重命名失败", err, "确定"); else { _header.SetAsset(asset); if (_activeTab == 0) _catalogPane.Invalidate(); else _charmPane.Invalidate(); } } // ── CharmCatalogSO 详情 ─────────────────────────────────────────────── private VisualElement BuildCatalogActionBar(CharmCatalogSO catalog) { var bar = SkillModule.MakeActionBar(); new Button(() => { EditorGUIUtility.PingObject(catalog); Selection.activeObject = catalog; }) { text = "定位" }.AlsoAddTo(bar); return bar; } // ── CharmSO 详情 ────────────────────────────────────────────────────── private static VisualElement BuildCharmCard(CharmSO c) { var card = SkillModule.MakeCard(); SkillModule.AddChip(card, "ID", string.IsNullOrEmpty(c.charmId) ? "-" : c.charmId); SkillModule.AddChip(card, "格数", $"{c.notchCost}"); SkillModule.AddChip(card, "效果数", $"{c.effects?.Count ?? 0}"); if (c.isUnique) SkillModule.AddChip(card, "限定", "唯一"); return card; } private VisualElement BuildCharmActionBar(CharmSO charm) { var bar = SkillModule.MakeActionBar(); new Button(() => { EditorGUIUtility.PingObject(charm); Selection.activeObject = charm; }) { text = "定位" }.AlsoAddTo(bar); new Button(() => { var c = AssetOperations.Clone(charm, CharmFolder); if (c != null) _charmPane.Refresh(c); }) { text = "克隆..." }.AlsoAddTo(bar); var del = new Button(() => { if (AssetOperations.Delete(charm)) _charmPane.Refresh(null); }) { text = "删除" }; ApplyDeleteStyle(del); del.AlsoAddTo(bar); return bar; } // ── 共用 ───────────────────────────────────────────────────────────── private static void ApplyDeleteStyle(Button btn) { var c = new StyleColor(new Color(0.8f, 0.3f, 0.3f, 0.6f)); btn.style.borderLeftColor = c; btn.style.borderRightColor = c; btn.style.borderTopColor = c; btn.style.borderBottomColor = c; btn.style.borderLeftWidth = 1; btn.style.borderRightWidth = 1; btn.style.borderTopWidth = 1; btn.style.borderBottomWidth = 1; btn.style.marginLeft = 8; } } }