using System.Collections.Generic; using UnityEditor; using UnityEngine; namespace BaseGames.Editor.Modules { /// /// 批量验证结果窗口:以可滚动列表展示各项配置问题, /// 每条记录附带"选中"按钮,点击后高亮并选中对应资产(EditorGUIUtility.PingObject)。 /// 支持 Error/Warning Tab 切换与文本搜索过滤。 /// 由各验证模块(QuestModule、DialogueModule、NpcModule)在检测到问题时弹出。 /// internal class QuestValidationResultWindow : EditorWindow { internal struct Issue { public string message; public bool isError; public UnityEngine.Object asset; // null = 无对应资产(如孤儿触发器) } private List _issues; private int _errorCount; private int _warnCount; private int _totalItems; private string _itemLabel = "资产"; private Vector2 _scroll; // ── 过滤状态 ───────────────────────────────────────────────────────── private enum TabMode { All, ErrorsOnly, WarnsOnly } private TabMode _tab = TabMode.All; private string _filter = ""; private static readonly GUIStyle s_tabActive = null; // 延迟初始化 private static readonly GUIStyle s_tabInactive = null; // ── 打开入口 ────────────────────────────────────────────────────────── internal static void Show(List issues, int errorCount, int warnCount, int totalItems, string windowTitle = "批量验证结果", string itemLabel = "资产") { var win = GetWindow(true, windowTitle, true); win._issues = issues; win._errorCount = errorCount; win._warnCount = warnCount; win._totalItems = totalItems; win._itemLabel = itemLabel; win._tab = TabMode.All; win._filter = ""; win.minSize = new Vector2(560, 380); win.Show(); } // ── 绘制 ────────────────────────────────────────────────────────────── private void OnGUI() { if (_issues == null) { EditorGUILayout.LabelField("无数据。"); return; } bool clean = _errorCount == 0 && _warnCount == 0; // ── 标题摘要 ── EditorGUILayout.Space(6); var summaryStyle = new GUIStyle(EditorStyles.boldLabel) { fontSize = 12 }; if (clean) summaryStyle.normal.textColor = new Color(0.2f, 0.75f, 0.2f); string prefix = clean ? "✅ " : (_errorCount > 0 ? "❌ " : "⚠ "); string summary = $"{prefix}验证完成:{_totalItems} 个{_itemLabel} · {_errorCount} 个错误 · {_warnCount} 个警告"; EditorGUILayout.LabelField(summary, summaryStyle); EditorGUILayout.Space(4); if (clean) { EditorGUILayout.LabelField($"所有 {_itemLabel} 配置均合法!", EditorStyles.centeredGreyMiniLabel); return; } // ── 分隔线 ── var divRect = EditorGUILayout.GetControlRect(false, 1); EditorGUI.DrawRect(divRect, new Color(0.35f, 0.35f, 0.35f)); EditorGUILayout.Space(4); // ── Tab 切换 + 搜索框 ── EditorGUILayout.BeginHorizontal(); DrawTab("全部", TabMode.All, _issues.Count); DrawTab("错误", TabMode.ErrorsOnly, _errorCount); DrawTab("警告", TabMode.WarnsOnly, _warnCount); GUILayout.FlexibleSpace(); GUILayout.Label("🔍", GUILayout.Width(18)); _filter = EditorGUILayout.TextField(_filter, GUILayout.Width(180)); if (GUILayout.Button("×", GUILayout.Width(22))) _filter = ""; EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(4); // ── 问题列表 ── _scroll = EditorGUILayout.BeginScrollView(_scroll); int shown = 0; foreach (var issue in _issues) { if (!MatchesFilter(issue)) continue; shown++; DrawIssueRow(issue); EditorGUILayout.Space(2); } if (shown == 0) EditorGUILayout.LabelField("(当前过滤条件下无结果)", EditorStyles.centeredGreyMiniLabel); EditorGUILayout.EndScrollView(); // ── 底部状态栏 ── EditorGUILayout.Space(2); var statusRect = EditorGUILayout.GetControlRect(false, 1); EditorGUI.DrawRect(statusRect, new Color(0.3f, 0.3f, 0.3f)); EditorGUILayout.LabelField( $"显示 {shown} / {_issues.Count} 条", EditorStyles.centeredGreyMiniLabel); } // ── 辅助方法 ───────────────────────────────────────────────────────── private bool MatchesFilter(Issue issue) { if (_tab == TabMode.ErrorsOnly && !issue.isError) return false; if (_tab == TabMode.WarnsOnly && issue.isError) return false; if (!string.IsNullOrEmpty(_filter)) { bool msgMatch = issue.message.Contains(_filter, System.StringComparison.OrdinalIgnoreCase); bool assetMatch = issue.asset != null && issue.asset.name.Contains(_filter, System.StringComparison.OrdinalIgnoreCase); if (!msgMatch && !assetMatch) return false; } return true; } private void DrawTab(string label, TabMode mode, int count) { bool active = _tab == mode; var style = active ? new GUIStyle(EditorStyles.miniButtonMid) { fontStyle = FontStyle.Bold } : EditorStyles.miniButtonMid; if (active) style.normal.textColor = new Color(0.4f, 0.8f, 1f); string text = $"{label} ({count})"; if (GUILayout.Button(text, style, GUILayout.MinWidth(72))) _tab = mode; } private static void DrawIssueRow(Issue issue) { EditorGUILayout.BeginHorizontal(EditorStyles.helpBox); // 图标 var iconContent = issue.isError ? EditorGUIUtility.IconContent("console.erroricon.sml") : EditorGUIUtility.IconContent("console.warnicon.sml"); GUILayout.Label(iconContent, GUILayout.Width(20), GUILayout.Height(18)); // 消息文字 EditorGUILayout.LabelField(issue.message, EditorStyles.wordWrappedLabel); // 定位按钮(有资产引用时显示) if (issue.asset != null) { if (GUILayout.Button("选中", GUILayout.Width(40), GUILayout.Height(18))) { EditorGUIUtility.PingObject(issue.asset); Selection.activeObject = issue.asset; } } EditorGUILayout.EndHorizontal(); } } }