using System; using UnityEditor; using UnityEditor.UIElements; using UnityEngine; using UnityEngine.UIElements; using BaseGames.Skills; namespace BaseGames.Editor.Modules { /// /// DataHub 技能模块 —— 管理 FormSkillSO 资产。 /// public class SkillModule : IDataModule, IDataModuleOrdered { private const string Folder = "Assets/_Game/Data/Skills"; private const string Prefix = "SKL_"; public string ModuleId => "skill"; public string DisplayName => "技能"; public string IconName => null; public int DisplayOrder => 20; private SoListPane _listPane; private DetailHeader _header; private FormSkillSO _selected; public void Initialize() { _listPane = new SoListPane( Folder, Prefix, s => s.resourceType.ToString()); } public void BuildListPane(VisualElement container, Action onSelected) { _listPane.SelectionChanged = sel => { _selected = sel; onSelected?.Invoke(sel); }; container.Add(_listPane); _listPane.Refresh(); } public void BuildDetailPane(VisualElement container, UnityEngine.Object selected) { _selected = selected as FormSkillSO; _header = new DetailHeader(); _header.SetAsset(_selected); _header.RenameRequested += OnRenameRequested; container.Add(_header); if (_selected == null) return; // Stats Card var card = BuildStatsCard(_selected); container.Add(card); // 操作按钮 container.Add(BuildActionBar(_selected)); container.Add(MakeDivider()); // Inspector var insp = new InspectorElement(_selected); container.Add(insp); } public void OnActivated() => _listPane?.Refresh(); // ── 内部 ───────────────────────────────────────────────────────────── private void OnRenameRequested(string newName) { if (_selected == null) return; var (ok, err) = AssetOperations.Rename(_selected, newName); if (!ok) EditorUtility.DisplayDialog("重命名失败", err, "确定"); else { _header.SetAsset(_selected); _listPane.Invalidate(); } } private static VisualElement BuildStatsCard(FormSkillSO s) { var card = MakeCard(); AddChip(card, "效果类型", s.effectType.ToString()); AddChip(card, "资源类型", s.resourceType.ToString()); AddChip(card, "冷却", $"{s.cooldown:F1}s"); AddChip(card, "消耗", s.baseCost.ToString()); if (!string.IsNullOrEmpty(s.skillId)) AddChip(card, "ID", s.skillId); return card; } private VisualElement BuildActionBar(FormSkillSO s) { var bar = MakeActionBar(); new Button(() => { EditorGUIUtility.PingObject(s); Selection.activeObject = s; }) { text = "定位" }.AlsoAddTo(bar); new Button(() => { var c = AssetOperations.Clone(s, Folder); if (c != null) _listPane.Refresh(c); }) { text = "克隆..." }.AlsoAddTo(bar); var del = new Button(() => { if (AssetOperations.Delete(s)) _listPane.Refresh(null); }) { text = "删除" }; ApplyDeleteStyle(del); del.AlsoAddTo(bar); return bar; } // ── 共享构建辅助 ───────────────────────────────────────────────────── internal static VisualElement MakeCard() { var c = new VisualElement(); c.style.flexDirection = FlexDirection.Row; c.style.flexWrap = Wrap.Wrap; c.style.paddingLeft = 12; c.style.paddingRight = 12; c.style.paddingTop = 8; c.style.paddingBottom = 8; c.style.marginBottom = 4; c.style.backgroundColor = new StyleColor(new Color(0.5f, 0.5f, 0.5f, 0.08f)); c.style.borderBottomWidth = 1; c.style.borderBottomColor = new StyleColor(new Color(0.5f, 0.5f, 0.5f, 0.2f)); return c; } internal static void AddChip(VisualElement parent, string label, string value) { var chip = new VisualElement(); chip.style.flexDirection = FlexDirection.Row; chip.style.alignItems = Align.Center; chip.style.marginRight = 14; chip.style.marginBottom = 2; var l = new Label(label + ":"); l.style.opacity = 0.6f; l.style.fontSize = 11; l.style.marginRight = 3; chip.Add(l); var v = new Label(value); v.style.fontSize = 11; v.style.unityFontStyleAndWeight = UnityEngine.FontStyle.Bold; chip.Add(v); parent.Add(chip); } internal static VisualElement MakeActionBar() { var b = new VisualElement(); b.style.flexDirection = FlexDirection.Row; b.style.paddingLeft = 12; b.style.paddingRight = 12; b.style.paddingTop = 6; b.style.paddingBottom = 6; b.style.flexWrap = Wrap.Wrap; return b; } internal static VisualElement MakeDivider() { var d = new VisualElement(); d.style.height = 1; d.style.backgroundColor = new StyleColor(new Color(0.5f, 0.5f, 0.5f, 0.2f)); return d; } /// 将删除按钮染成红色边框,统一各模块样式。 internal static void ApplyDeleteStyle(Button btn) { var red = new StyleColor(new Color(0.8f, 0.3f, 0.3f, 0.6f)); btn.style.borderLeftColor = red; btn.style.borderRightColor = red; btn.style.borderTopColor = red; btn.style.borderBottomColor = red; btn.style.borderLeftWidth = 1; btn.style.borderRightWidth = 1; btn.style.borderTopWidth = 1; btn.style.borderBottomWidth = 1; btn.style.marginLeft = 8; } /// /// 为任意 ScriptableObject 模块生成标准 ActionBar(新建 / 定位 / 克隆 / 删除)。 /// 各模块可在返回后向 bar 追加额外按钮。 /// /// 当前选中资产。 /// 资产所在文件夹(用于新建 / 克隆)。 /// 新建资产的文件名前缀。 /// 新建完成回调(传入新资产)。 /// 克隆完成回调(传入克隆资产)。 /// 删除完成回调。 internal static VisualElement BuildStandardActionBar( T asset, string folder, string prefix, Action onCreated, Action onCloned, Action onDeleted, Action> wizardCreate = null) where T : UnityEngine.ScriptableObject { var bar = MakeActionBar(); new Button(() => { if (wizardCreate != null) { wizardCreate(c => onCreated?.Invoke(c)); } else { var c = AssetOperations.Create(folder, prefix + "New"); if (c != null) onCreated?.Invoke(c); } }) { text = "新建" }.AlsoAddTo(bar); new Button(() => { EditorGUIUtility.PingObject(asset); Selection.activeObject = asset; }) { text = "定位" }.AlsoAddTo(bar); new Button(() => { var c = AssetOperations.Clone(asset, folder); if (c != null) onCloned?.Invoke(c); }) { text = "克隆..." }.AlsoAddTo(bar); var del = new Button(() => { if (AssetOperations.Delete(asset)) onDeleted?.Invoke(); }) { text = "删除" }; ApplyDeleteStyle(del); del.AlsoAddTo(bar); return bar; } } // ── Button 扩展(模块内共用)───────────────────────────────────────────── internal static class ButtonExtensions { public static Button AlsoAddTo(this Button btn, VisualElement parent) { parent.Add(btn); return btn; } } }