using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using BaseGames.UI;
using BaseGames.UI.Theme;
using BaseGames.Core.Events;
using BaseGames.Localization;
using BaseGames.Editor.Localization;
namespace BaseGames.Editor.UI
{
///
/// 护符面板脚手架:生成 UI_CharmPanel 预制件(CharmEquipPanel + 已装备区 + 收藏目录 + 凹槽进度条 + 卡片模板)。
/// 数据来源运行期 ServiceLocator → IEquipmentService(EquipmentManager);变更经 EVT_EquipmentChanged 反应式重建。
///
/// 该面板既可 standalone 打开(PanelId.CharmPanel),也由 InventoryHub 内嵌为「护符」Tab(脚手架内嵌时中和 Canvas 链 + 隐藏 chrome)。
/// 美术 → 改 UI_CharmPanel / 卡片样式;策划 → 改护符数据(CharmSO/CharmCatalogSO)。
/// 菜单:BaseGames/UI/控件库/生成护符面板(预制件)
///
public static class UICharmScaffold
{
private const string PrefabDir = "Assets/_Game/Prefabs/UI";
private const string ThemePath = "Assets/_Game/Data/UI/Themes/UI_Theme_Default.asset";
private const string PrefabName = "UI_CharmPanel";
[MenuItem("BaseGames/UI/控件库/生成护符面板(预制件)")]
public static void GeneratePrefab()
{
EnsureFolder(PrefabDir);
var report = new List();
var theme = AssetDatabase.LoadAssetAtPath(ThemePath);
SeedLocalization(report);
BuildPrefab(theme, report);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
var sb = new System.Text.StringBuilder("[UICharm] 护符面板已生成:\n");
foreach (var r in report) sb.AppendLine(" • " + r);
sb.AppendLine("运行期读 IEquipmentService(EquipmentManager);重跑「Scaffold Inventory Hub」把它收编为护符 Tab。");
Debug.Log(sb.ToString());
}
private static void SeedLocalization(List report)
{
var zh = new Dictionary
{
{ "MENU_CHARM", "护符" }, { "CHARM_TITLE", "护符" },
{ "CHARM_EQUIPPED", "已装备" }, { "CHARM_CATALOG", "收藏" }, { "CHARM_NOTCHES", "凹槽" },
};
var en = new Dictionary
{
{ "MENU_CHARM", "Charms" }, { "CHARM_TITLE", "Charms" },
{ "CHARM_EQUIPPED", "Equipped" }, { "CHARM_CATALOG", "Collected" }, { "CHARM_NOTCHES", "Notches" },
};
int added = MergeWriteUI(Language.ChineseSimplified, zh) + MergeWriteUI(Language.English, en);
report.Add($"本地化新增 {added} 条(已存在不覆盖)。");
}
private static int MergeWriteUI(Language lang, Dictionary kv)
{
var dict = LocalizationFileIO.Read(lang, LocalizationTable.UI);
int added = 0;
foreach (var p in kv)
if (!dict.ContainsKey(p.Key)) { dict[p.Key] = p.Value; added++; }
if (added > 0) LocalizationFileIO.Write(lang, LocalizationTable.UI, dict);
return added;
}
// ── 预制件 ───────────────────────────────────────────────────────────
private static void BuildPrefab(UIThemeSO theme, List report)
{
var root = new GameObject(PrefabName,
typeof(RectTransform), typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster), typeof(CanvasGroup));
Stretch((RectTransform)root.transform);
SetupCanvas(root, 61);
var panel = root.AddComponent();
var applier = root.AddComponent();
if (theme != null) AssignRef(applier, "_theme", theme);
// 全屏遮罩(chrome:内嵌时隐藏)
var overlayGo = NewUIChild(root.transform, "Overlay", out var overlayRt);
Stretch(overlayRt);
var overlay = overlayGo.AddComponent(); overlay.color = new Color(0f, 0f, 0f, 0.6f);
// 主框
var boxGo = NewUIChild(root.transform, "DialogBox", out var boxRt);
SetRect(boxRt, C(0.5f), C(0.5f), C(0.5f), Vector2.zero, new Vector2(1280f, 720f));
var box = boxGo.AddComponent();
box.sprite = Std(); box.type = Image.Type.Sliced; box.color = new Color(0.10f, 0.11f, 0.15f, 0.98f);
SetEnum(boxGo.AddComponent(), "_kind", (int)UIThemeRoleKind.Graphic_Background);
// 标题
var titleGo = NewUIChild(boxGo.transform, "Title", out var titleRt);
SetRect(titleRt, C(0.5f, 1f), C(0.5f, 1f), C(0.5f, 1f), new Vector2(0f, -50f), new Vector2(600f, 60f));
var title = titleGo.AddComponent();
title.alignment = TextAlignmentOptions.Center; title.fontSize = 40f; title.raycastTarget = false;
titleGo.AddComponent();
SetString(titleGo.GetComponent(), "_key", "CHARM_TITLE");
SetEnum(titleGo.AddComponent(), "_kind", (int)UIThemeRoleKind.Text_Header);
// 凹槽进度条(已用/总数)
var notchGo = NewUIChild(boxGo.transform, "NotchBar", out var notchRt);
SetRect(notchRt, C(0.5f, 1f), C(0.5f, 1f), C(0.5f, 1f), new Vector2(0f, -110f), new Vector2(420f, 34f));
var notchBg = notchGo.AddComponent(); notchBg.color = new Color(1f, 1f, 1f, 0.08f);
notchBg.sprite = Std(); notchBg.type = Image.Type.Sliced;
var fillGo = NewUIChild(notchGo.transform, "Fill", out var fillRt);
Stretch(fillRt);
var notchFill = fillGo.AddComponent();
notchFill.color = new Color(0.85f, 0.80f, 0.55f, 0.85f);
notchFill.sprite = Std(); notchFill.type = Image.Type.Filled;
notchFill.fillMethod = Image.FillMethod.Horizontal; notchFill.fillOrigin = 0; notchFill.fillAmount = 0f;
var notchTextGo = NewUIChild(notchGo.transform, "NotchText", out var notchTextRt);
Stretch(notchTextRt);
var notchText = notchTextGo.AddComponent();
notchText.alignment = TextAlignmentOptions.Center; notchText.fontSize = 20f; notchText.raycastTarget = false;
SetEnum(notchTextGo.AddComponent(), "_kind", (int)UIThemeRoleKind.Text_Primary);
// 已装备区(左)
var equippedLabel = MakeSectionLabel(boxGo.transform, "EquippedLabel", "CHARM_EQUIPPED",
C(0f, 1f), new Vector2(60f, -160f), new Vector2(420f, 34f));
var equippedGo = NewUIChild(boxGo.transform, "EquippedContainer", out var equippedRt);
SetRect(equippedRt, C(0f, 1f), C(0f, 1f), C(0f, 1f), new Vector2(40f, -200f), new Vector2(440f, 460f));
var eqGrid = equippedGo.AddComponent();
eqGrid.cellSize = new Vector2(200f, 96f); eqGrid.spacing = new Vector2(8f, 8f);
eqGrid.childAlignment = TextAnchor.UpperLeft; eqGrid.constraint = GridLayoutGroup.Constraint.FixedColumnCount;
eqGrid.constraintCount = 2;
// 收藏目录(右,滚动)
var catalogLabel = MakeSectionLabel(boxGo.transform, "CatalogLabel", "CHARM_CATALOG",
C(1f, 1f), new Vector2(-490f, -160f), new Vector2(440f, 34f));
var scrollGo = NewUIChild(boxGo.transform, "CatalogScroll", out var scrollRt);
SetRect(scrollRt, C(1f, 1f), C(1f, 1f), C(1f, 1f), new Vector2(-40f, -200f), new Vector2(680f, 460f));
var scrollImg = scrollGo.AddComponent(); scrollImg.color = new Color(1f, 1f, 1f, 0.03f);
var scroll = scrollGo.AddComponent();
scroll.horizontal = false; scroll.vertical = true;
var viewportGo = NewUIChild(scrollGo.transform, "Viewport", out var viewportRt);
Stretch(viewportRt);
var vpImg = viewportGo.AddComponent(); vpImg.color = new Color(1f, 1f, 1f, 0.01f);
viewportGo.AddComponent();
var catalogGo = NewUIChild(viewportGo.transform, "CatalogContainer", out var catalogRt);
SetRect(catalogRt, C(0f, 1f), C(1f, 1f), C(0.5f, 1f), Vector2.zero, new Vector2(0f, 0f));
var catGrid = catalogGo.AddComponent();
catGrid.cellSize = new Vector2(200f, 96f); catGrid.spacing = new Vector2(8f, 8f);
catGrid.childAlignment = TextAnchor.UpperLeft; catGrid.constraint = GridLayoutGroup.Constraint.FixedColumnCount;
catGrid.constraintCount = 3; catGrid.padding = new RectOffset(8, 8, 8, 8);
var catFitter = catalogGo.AddComponent();
catFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
AssignRef(scroll, "m_Content", catalogRt);
AssignRef(scroll, "m_Viewport", viewportRt);
// 卡片模板(CharmCardView,inactive)
var cardGo = BuildCardTemplate(boxGo.transform);
// 关闭按钮(chrome:内嵌时隐藏)
var closeGo = NewUIChild(boxGo.transform, "Btn_Close", out var closeRt);
SetRect(closeRt, C(0.5f, 0f), C(0.5f, 0f), C(0.5f, 0f), new Vector2(0f, 36f), new Vector2(240f, 56f));
var closeImg = closeGo.AddComponent(); closeImg.sprite = Std(); closeImg.type = Image.Type.Sliced;
closeImg.color = new Color(1f, 1f, 1f, 0.06f);
var closeBtn = closeGo.AddComponent