627 lines
29 KiB
C#
627 lines
29 KiB
C#
#if UNITY_EDITOR
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Text.RegularExpressions;
|
||
using UnityEditor;
|
||
using UnityEngine;
|
||
|
||
namespace Continentis.MainGame.Card
|
||
{
|
||
/// <summary>
|
||
/// 卡牌成品预览窗口。
|
||
/// 通过 CardDataEditor 中的"预览卡牌"按钮打开,或通过菜单 Continentis / Mod Tools / 卡牌预览 进入。
|
||
/// 使用 IMGUI 多层合成方式还原游戏内卡牌的大致外观。
|
||
/// </summary>
|
||
public class CardPreviewer : EditorWindow
|
||
{
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
// 常量与路径
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
|
||
private const string k_SpritesRoot = "Assets/Sprites/MainGame/CardView/GeneralCard";
|
||
private const string k_LocaleRoot = "Assets/Mods";
|
||
|
||
// CSV 列索引(对应 CSV 首行:Key, English, Simplified Chinese, ...)
|
||
private const int k_ColEN = 1;
|
||
private const int k_ColCN = 2;
|
||
|
||
// 卡牌宽高比(基于参考图:约 2:3)
|
||
private const float k_CardAspect = 2f / 3f; // width / height
|
||
|
||
// 卡牌内部各区域相对于卡牌高度的比例(基于参考截图目测)
|
||
private const float k_ArtTopRatio = 0.09f; // 图片区顶部
|
||
private const float k_ArtBotRatio = 0.53f; // 图片区底部(包含类型徽章重叠)
|
||
private const float k_NameTopRatio = 0.065f; // 名称文字中心
|
||
private const float k_TypeBadgeRatio = 0.525f; // 类型徽章中心
|
||
private const float k_DescTopRatio = 0.565f; // 描述文字区顶部
|
||
private const float k_DescBotRatio = 0.92f; // 描述文字区底部
|
||
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
// 状态
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
|
||
private CardData _cardData;
|
||
|
||
// 语言切换工具栏(0=English, 1=简体中文)
|
||
private int _langIndex = 0;
|
||
private readonly string[] _langLabels = { "English", "简体中文" };
|
||
|
||
// 加载的精灵层
|
||
private Texture2D _texBackground;
|
||
private Texture2D _texOuterFrame;
|
||
private Texture2D _texNameFrame;
|
||
private Texture2D _texTypeBg;
|
||
private Texture2D _texTypeMain;
|
||
|
||
// 本地化缓存:key → [EN, CN]
|
||
private Dictionary<string, string[]> _localizationCache;
|
||
private bool _localeLoaded = false;
|
||
|
||
// 样式缓存(延迟初始化,避免 OnEnable 时 GUI skin 未就绪)
|
||
private GUIStyle _cardNameStyle;
|
||
private GUIStyle _descStyle;
|
||
private GUIStyle _typeStyle;
|
||
private bool _stylesInitialized = false;
|
||
|
||
// 滚动位置(描述文本过长时)
|
||
private Vector2 _scrollPos;
|
||
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
// 静态入口
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
|
||
[MenuItem("Continentis/Mod Tools/卡牌预览")]
|
||
public static void OpenEmpty()
|
||
{
|
||
GetOrCreateWindow(null);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从 CardDataEditor 调用,附带要预览的 CardData。
|
||
/// </summary>
|
||
public static void OpenWith(CardData cardData)
|
||
{
|
||
GetOrCreateWindow(cardData);
|
||
}
|
||
|
||
private static CardPreviewer GetOrCreateWindow(CardData cardData)
|
||
{
|
||
var window = GetWindow<CardPreviewer>(false, "卡牌预览", true);
|
||
window.minSize = new Vector2(280, 480);
|
||
window.maxSize = new Vector2(500, 900);
|
||
if (cardData != null)
|
||
window.SetTarget(cardData);
|
||
return window;
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
// 生命周期
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
|
||
private void OnEnable()
|
||
{
|
||
LoadLayerSprites();
|
||
}
|
||
|
||
private void OnDisable()
|
||
{
|
||
// 不主动 Destroy —— AssetDatabase 加载的对象由 Unity 管理生命周期
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
// 公开接口
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
|
||
public void SetTarget(CardData cardData)
|
||
{
|
||
_cardData = cardData;
|
||
_localeLoaded = false; // 切换目标时清空缓存,触发重新扫描
|
||
Repaint();
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
// OnGUI — 主绘制
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
|
||
private void OnGUI()
|
||
{
|
||
EnsureStylesInitialized();
|
||
EnsureLocalizationLoaded();
|
||
|
||
DrawToolbar();
|
||
|
||
if (_cardData == null)
|
||
{
|
||
DrawEmptyState();
|
||
return;
|
||
}
|
||
|
||
DrawCardPreview();
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
// 工具栏(语言切换 + 选择 CardData)
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
|
||
private void DrawToolbar()
|
||
{
|
||
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
||
|
||
// 语言切换
|
||
EditorGUILayout.LabelField("语言:", GUILayout.Width(36));
|
||
int newLang = GUILayout.Toolbar(_langIndex, _langLabels, EditorStyles.toolbarButton, GUILayout.Width(160));
|
||
if (newLang != _langIndex)
|
||
{
|
||
_langIndex = newLang;
|
||
Repaint();
|
||
}
|
||
|
||
GUILayout.FlexibleSpace();
|
||
|
||
// 手动选择 CardData
|
||
if (GUILayout.Button("选择 CardData...", EditorStyles.toolbarButton, GUILayout.Width(120)))
|
||
{
|
||
ShowCardDataPicker();
|
||
}
|
||
|
||
EditorGUILayout.EndHorizontal();
|
||
|
||
// CardData 对象字段(次行,方便拖拽)
|
||
EditorGUI.BeginChangeCheck();
|
||
var picked = (CardData)EditorGUILayout.ObjectField(
|
||
"CardData", _cardData, typeof(CardData), false);
|
||
if (EditorGUI.EndChangeCheck())
|
||
SetTarget(picked);
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
// 空状态提示
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
|
||
private void DrawEmptyState()
|
||
{
|
||
GUILayout.FlexibleSpace();
|
||
EditorGUILayout.LabelField(
|
||
"请将 CardData 拖入上方字段,\n或点击\"选择 CardData...\"按钮",
|
||
new GUIStyle(EditorStyles.centeredGreyMiniLabel) { fontSize = 12, wordWrap = true },
|
||
GUILayout.ExpandWidth(true));
|
||
GUILayout.FlexibleSpace();
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
// 卡牌合成渲染
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
|
||
private void DrawCardPreview()
|
||
{
|
||
// ── 计算卡牌绘制区域(居中,保持宽高比)──────────────────────────
|
||
float availW = position.width - 16f;
|
||
float availH = position.height - 60f; // 减去工具栏高度
|
||
float cardW, cardH;
|
||
|
||
if (availW / availH < k_CardAspect)
|
||
{
|
||
cardW = availW;
|
||
cardH = availW / k_CardAspect;
|
||
}
|
||
else
|
||
{
|
||
cardH = availH;
|
||
cardW = availH * k_CardAspect;
|
||
}
|
||
|
||
float offsetX = (availW - cardW) * 0.5f + 8f;
|
||
float offsetY = 58f; // 工具栏 + ObjectField 高度之和
|
||
|
||
Rect cardRect = new Rect(offsetX, offsetY, cardW, cardH);
|
||
|
||
// ── 层 1:卡牌深色背景 ────────────────────────────────────────────
|
||
if (_texBackground != null)
|
||
GUI.DrawTexture(cardRect, _texBackground, ScaleMode.StretchToFill, true);
|
||
else
|
||
EditorGUI.DrawRect(cardRect, new Color(0.08f, 0.08f, 0.12f));
|
||
|
||
// ── 层 2:卡牌图片(上半区域,内框范围内)────────────────────────
|
||
Rect artRect = RectFromRatios(cardRect,
|
||
padH: 0.04f, padV: k_ArtTopRatio,
|
||
widthRatio: 0.92f,
|
||
heightRatio: k_ArtBotRatio - k_ArtTopRatio);
|
||
|
||
if (_cardData.cardSprite != null)
|
||
{
|
||
GUI.DrawTexture(artRect, _cardData.cardSprite.texture, ScaleMode.ScaleToFit, true);
|
||
}
|
||
else
|
||
{
|
||
EditorGUI.DrawRect(artRect, new Color(0.15f, 0.15f, 0.2f));
|
||
GUI.Label(artRect, " 无卡牌图片",
|
||
new GUIStyle(EditorStyles.centeredGreyMiniLabel) { alignment = TextAnchor.MiddleCenter });
|
||
}
|
||
|
||
// ── 层 4:外框 ────────────────────────────────────────────────────
|
||
if (_texOuterFrame != null)
|
||
GUI.DrawTexture(cardRect, _texOuterFrame, ScaleMode.StretchToFill, true);
|
||
|
||
// ── 层 5:名称框背景 ──────────────────────────────────────────────
|
||
if (_texNameFrame != null)
|
||
{
|
||
Rect nameFrameRect = RectFromRatios(cardRect,
|
||
padH: 0.1f, padV: k_NameTopRatio - 0.036f,
|
||
widthRatio: 0.80f,
|
||
heightRatio: 0.072f);
|
||
GUI.DrawTexture(nameFrameRect, _texNameFrame, ScaleMode.StretchToFill, true);
|
||
}
|
||
|
||
// ── 层 6:卡牌名称文字 ────────────────────────────────────────────
|
||
string cardName = GetLocalizedText(_cardData.displayName);
|
||
Rect nameTextRect = new Rect(
|
||
cardRect.x + cardRect.width * 0.1f,
|
||
cardRect.y + cardRect.height * (k_NameTopRatio - 0.025f),
|
||
cardRect.width * 0.80f,
|
||
cardRect.height * 0.055f);
|
||
GUI.Label(nameTextRect, cardName, _cardNameStyle);
|
||
|
||
// ── 层 7:类型徽章 ────────────────────────────────────────────────
|
||
Rect typeBadgeRect = RectFromRatios(cardRect,
|
||
padH: 0.25f, padV: k_TypeBadgeRatio - 0.028f,
|
||
widthRatio: 0.50f,
|
||
heightRatio: 0.056f);
|
||
|
||
if (_texTypeBg != null)
|
||
GUI.DrawTexture(typeBadgeRect, _texTypeBg, ScaleMode.StretchToFill, true);
|
||
if (_texTypeMain != null)
|
||
GUI.DrawTexture(typeBadgeRect, _texTypeMain, ScaleMode.StretchToFill, true);
|
||
|
||
GUI.Label(typeBadgeRect, _cardData.cardType.ToString(), _typeStyle);
|
||
|
||
// ── 层 8:描述文字区(滚动) ──────────────────────────────────────
|
||
Rect descArea = new Rect(
|
||
cardRect.x + cardRect.width * 0.06f,
|
||
cardRect.y + cardRect.height * k_DescTopRatio,
|
||
cardRect.width * 0.88f,
|
||
cardRect.height * (k_DescBotRatio - k_DescTopRatio));
|
||
|
||
string rawFunction = GetLocalizedText(_cardData.functionText);
|
||
string displayDesc = StripMacros(rawFunction);
|
||
|
||
// 动态调整字号使文字适应区域
|
||
_descStyle.fontSize = CalculateFontSize(cardH);
|
||
|
||
// 用 Scroll View 防止超长文字溢出
|
||
_scrollPos = GUI.BeginScrollView(descArea, _scrollPos,
|
||
new Rect(0, 0, descArea.width - 10f,
|
||
Mathf.Max(descArea.height,
|
||
_descStyle.CalcHeight(new GUIContent(displayDesc), descArea.width - 14f))));
|
||
GUI.Label(new Rect(0, 4f, descArea.width - 14f, descArea.height + 200f),
|
||
displayDesc, _descStyle);
|
||
GUI.EndScrollView();
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
// 资源加载
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
|
||
private void LoadLayerSprites()
|
||
{
|
||
_texBackground = LoadTex("CardBackground_Default");
|
||
_texOuterFrame = LoadTex("CardOuterFrame");
|
||
_texNameFrame = LoadTex("CardNameFrame");
|
||
_texTypeBg = LoadTex("CardTypeBackground");
|
||
_texTypeMain = LoadTex("CardTypeMain");
|
||
}
|
||
|
||
private static Texture2D LoadTex(string fileName)
|
||
{
|
||
// 优先加载 PNG,再尝试其他扩展名
|
||
string path = $"{k_SpritesRoot}/{fileName}.png";
|
||
var spr = AssetDatabase.LoadAssetAtPath<Sprite>(path);
|
||
if (spr != null) return spr.texture;
|
||
|
||
// 降级:直接加载 Texture2D
|
||
return AssetDatabase.LoadAssetAtPath<Texture2D>(path);
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
// 本地化加载(惰性 + 扫描所有 CSV)
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
|
||
private void EnsureLocalizationLoaded()
|
||
{
|
||
if (_localeLoaded) return;
|
||
_localizationCache = new Dictionary<string, string[]>(StringComparer.Ordinal);
|
||
|
||
string[] guids = AssetDatabase.FindAssets("t:TextAsset Localization", new[] { k_LocaleRoot });
|
||
foreach (string guid in guids)
|
||
{
|
||
string csvPath = AssetDatabase.GUIDToAssetPath(guid);
|
||
if (!csvPath.EndsWith(".csv", StringComparison.OrdinalIgnoreCase)) continue;
|
||
|
||
try
|
||
{
|
||
ParseCsv(File.ReadAllText(csvPath));
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.LogWarning($"[CardPreviewer] 解析本地化文件失败:{csvPath}\n{e.Message}");
|
||
}
|
||
}
|
||
|
||
_localeLoaded = true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 解析单个 CSV 文件,将 Key → [EN, CN] 写入缓存。
|
||
/// 支持 RFC 4180 标准的双引号转义字段。
|
||
/// </summary>
|
||
private void ParseCsv(string content)
|
||
{
|
||
// 按行拆分
|
||
string[] lines = content.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None);
|
||
if (lines.Length < 2) return;
|
||
|
||
// 首行为列头,找到 English / Simplified Chinese 的列索引
|
||
string[] headers = SplitCsvRow(lines[0]);
|
||
int colEN = FindColumnIndex(headers, "English");
|
||
int colCN = FindColumnIndex(headers, "Simplified Chinese");
|
||
|
||
for (int i = 1; i < lines.Length; i++)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(lines[i])) continue;
|
||
string[] cells = SplitCsvRow(lines[i]);
|
||
if (cells.Length == 0) continue;
|
||
|
||
string key = cells[0].Trim();
|
||
if (string.IsNullOrEmpty(key)) continue;
|
||
|
||
string en = colEN >= 0 && colEN < cells.Length ? cells[colEN] : string.Empty;
|
||
string cn = colCN >= 0 && colCN < cells.Length ? cells[colCN] : string.Empty;
|
||
|
||
_localizationCache[key] = new[] { en, cn };
|
||
}
|
||
}
|
||
|
||
private static int FindColumnIndex(string[] headers, string columnName)
|
||
{
|
||
for (int i = 0; i < headers.Length; i++)
|
||
if (string.Equals(headers[i].Trim(), columnName, StringComparison.OrdinalIgnoreCase))
|
||
return i;
|
||
return -1;
|
||
}
|
||
|
||
/// <summary>
|
||
/// RFC 4180 兼容的 CSV 行拆分,处理双引号包围的字段及内部双引号转义。
|
||
/// </summary>
|
||
private static string[] SplitCsvRow(string line)
|
||
{
|
||
var fields = new List<string>();
|
||
bool inQuote = false;
|
||
var current = new System.Text.StringBuilder();
|
||
|
||
for (int i = 0; i < line.Length; i++)
|
||
{
|
||
char c = line[i];
|
||
if (inQuote)
|
||
{
|
||
if (c == '"')
|
||
{
|
||
// 双引号转义 ("") → 单引号字符
|
||
if (i + 1 < line.Length && line[i + 1] == '"')
|
||
{
|
||
current.Append('"');
|
||
i++;
|
||
}
|
||
else
|
||
{
|
||
inQuote = false;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
current.Append(c);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (c == '"')
|
||
{
|
||
inQuote = true;
|
||
}
|
||
else if (c == ',')
|
||
{
|
||
fields.Add(current.ToString());
|
||
current.Clear();
|
||
}
|
||
else
|
||
{
|
||
current.Append(c);
|
||
}
|
||
}
|
||
}
|
||
fields.Add(current.ToString());
|
||
return fields.ToArray();
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
// 文本处理
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
|
||
/// <summary>
|
||
/// 根据当前语言返回本地化文本。若未找到,退回显示 Key 本身。
|
||
/// </summary>
|
||
private string GetLocalizedText(string key)
|
||
{
|
||
if (string.IsNullOrEmpty(key)) return string.Empty;
|
||
|
||
if (_localizationCache != null && _localizationCache.TryGetValue(key, out string[] texts))
|
||
{
|
||
string text = _langIndex == 0 ? texts[0] : texts[1];
|
||
return string.IsNullOrEmpty(text) ? $"[{key}]" : text;
|
||
}
|
||
return $"[{key}]"; // 未找到时显示 Key
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将功能文本中的宏替换为可读形式,用于在预览中近似展示。
|
||
/// $Keyword("xxx") → [xxx]
|
||
/// $Attribute("xxx") → {xxx}
|
||
/// </summary>
|
||
private static string StripMacros(string text)
|
||
{
|
||
if (string.IsNullOrEmpty(text)) return text;
|
||
text = Regex.Replace(text, @"\$Keyword\(""([^""]+)""\)", m => $"[{m.Groups[1].Value}]");
|
||
text = Regex.Replace(text, @"\$Attribute\(""([^""]+)""\)", m => $"{{{m.Groups[1].Value}}}");
|
||
return text;
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
// 样式初始化
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
|
||
private void EnsureStylesInitialized()
|
||
{
|
||
if (_stylesInitialized) return;
|
||
|
||
_cardNameStyle = new GUIStyle(GUI.skin.label)
|
||
{
|
||
fontSize = 16,
|
||
fontStyle = FontStyle.Bold,
|
||
alignment = TextAnchor.MiddleCenter,
|
||
wordWrap = false,
|
||
normal = { textColor = Color.white }
|
||
};
|
||
|
||
_typeStyle = new GUIStyle(GUI.skin.label)
|
||
{
|
||
fontSize = 12,
|
||
fontStyle = FontStyle.Bold,
|
||
alignment = TextAnchor.MiddleCenter,
|
||
wordWrap = false,
|
||
// 全部状态一律使用相同颜色,防止 Unity GUI skin 默认的悬停高亮
|
||
normal = { textColor = new Color(0.2f, 0.55f, 0.2f) },
|
||
hover = { textColor = new Color(0.2f, 0.55f, 0.2f) },
|
||
active = { textColor = new Color(0.2f, 0.55f, 0.2f) },
|
||
focused = { textColor = new Color(0.2f, 0.55f, 0.2f) }
|
||
};
|
||
|
||
_descStyle = new GUIStyle(GUI.skin.label)
|
||
{
|
||
fontSize = 13,
|
||
fontStyle = FontStyle.Normal,
|
||
alignment = TextAnchor.UpperLeft,
|
||
wordWrap = true,
|
||
richText = true,
|
||
normal = { textColor = new Color(0.88f, 0.88f, 0.88f) }
|
||
};
|
||
|
||
_stylesInitialized = true;
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
// 工具方法
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
|
||
/// <summary>
|
||
/// 在 baseRect 内部按比例偏移创建子矩形。
|
||
/// padH / padV 是左右/上方的水平/垂直内边距(相对于卡牌宽/高的比值)。
|
||
/// </summary>
|
||
private static Rect RectFromRatios(Rect baseRect, float padH, float padV,
|
||
float widthRatio, float heightRatio)
|
||
{
|
||
return new Rect(
|
||
baseRect.x + baseRect.width * padH,
|
||
baseRect.y + baseRect.height * padV,
|
||
baseRect.width * widthRatio,
|
||
baseRect.height * heightRatio);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据当前卡牌绘制高度动态计算字号,确保缩小窗口时文字不溢出。
|
||
/// </summary>
|
||
private static int CalculateFontSize(float cardHeight)
|
||
{
|
||
// 字号随卡牌高度线性缩放,base 400px → 13pt
|
||
int size = Mathf.RoundToInt(cardHeight * 13f / 400f);
|
||
return Mathf.Clamp(size, 9, 16);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 打开 Unity 内置的 CardData 类型选择器(Object Picker)。
|
||
/// </summary>
|
||
private void ShowCardDataPicker()
|
||
{
|
||
// 用 SearchService 或简单的 AssetDatabase 弹窗
|
||
string[] guids = AssetDatabase.FindAssets("t:CardData");
|
||
if (guids.Length == 0)
|
||
{
|
||
Debug.LogWarning("[CardPreviewer] 项目中未找到任何 CardData 资产。");
|
||
return;
|
||
}
|
||
|
||
// 收集所有 CardData 资产供选择
|
||
var items = new List<string>();
|
||
var paths = new List<string>();
|
||
foreach (string guid in guids)
|
||
{
|
||
string p = AssetDatabase.GUIDToAssetPath(guid);
|
||
items.Add(Path.GetFileNameWithoutExtension(p));
|
||
paths.Add(p);
|
||
}
|
||
|
||
// 弹出简易选择窗口
|
||
CardDataPickerWindow.Show(items, paths, selectedPath =>
|
||
{
|
||
var cd = AssetDatabase.LoadAssetAtPath<CardData>(selectedPath);
|
||
SetTarget(cd);
|
||
});
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
// 内部:CardData 选择弹窗
|
||
// ─────────────────────────────────────────────────────────────────────
|
||
|
||
private class CardDataPickerWindow : EditorWindow
|
||
{
|
||
private List<string> _names;
|
||
private List<string> _paths;
|
||
private Action<string> _onSelect;
|
||
private string _search = "";
|
||
private Vector2 _scroll;
|
||
|
||
public static void Show(List<string> names, List<string> paths, Action<string> onSelect)
|
||
{
|
||
var w = GetWindow<CardDataPickerWindow>(true, "选择 CardData", true);
|
||
w.minSize = new Vector2(260, 340);
|
||
w._names = names;
|
||
w._paths = paths;
|
||
w._onSelect = onSelect;
|
||
}
|
||
|
||
private void OnGUI()
|
||
{
|
||
GUILayout.BeginHorizontal(EditorStyles.toolbar);
|
||
_search = GUILayout.TextField(_search, GUI.skin.FindStyle("ToolbarSearchTextField") ?? EditorStyles.textField);
|
||
if (GUILayout.Button("", GUI.skin.FindStyle("ToolbarSearchCancelButton") ?? EditorStyles.miniButton))
|
||
_search = "";
|
||
GUILayout.EndHorizontal();
|
||
|
||
_scroll = EditorGUILayout.BeginScrollView(_scroll);
|
||
for (int i = 0; i < _names.Count; i++)
|
||
{
|
||
if (!string.IsNullOrEmpty(_search) &&
|
||
_names[i].IndexOf(_search, StringComparison.OrdinalIgnoreCase) < 0)
|
||
continue;
|
||
|
||
if (GUILayout.Button(_names[i], EditorStyles.label))
|
||
{
|
||
_onSelect?.Invoke(_paths[i]);
|
||
Close();
|
||
}
|
||
}
|
||
EditorGUILayout.EndScrollView();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#endif
|