using System; using System.Collections.Generic; using UnityEditor; using UnityEditor.UIElements; using UnityEngine.UIElements; namespace Module.ProjectValidator.Editor { internal sealed class EditorProjectValidatorWindow : EditorWindow { private MultiColumnTreeView _treeView; private string _searchFilter; private readonly List> _list = new(); private readonly List> _filteredList = new(); public void CreateGUI() { var root = rootVisualElement; var asset = EditorAssetUtility.LoadFirstAsset("UxmlEditorProjectValidatorWindow"); root.Add(asset.Instantiate()); root.Q("button-run").clicked += OnToolbarButtonRunClicked; root.Q("button-clear").clicked += OnToolbarButtonClearClicked; root.Q().RegisterValueChangedCallback(OnToolbarSearchFieldChanged); _treeView = root.Q(); _treeView.columns["asset"].makeCell = CreateObjectField; _treeView.columns["type"].makeCell = CreateLabel; _treeView.columns["severity"].makeCell = CreateLabel; _treeView.columns["severity-message"].makeCell = CreateLabel; _treeView.columns["scene-path"].makeCell = CreateLabel; _treeView.columns["field-path"].makeCell = CreateLabel; _treeView.columns["asset"].bindCell = OnTreeViewBindCellAsset; _treeView.columns["type"].bindCell = OnTreeViewBindCellType; _treeView.columns["severity"].bindCell = OnTreeViewBindCellSeverity; _treeView.columns["severity-message"].bindCell = OnTreeViewBindCellSeverityMessage; _treeView.columns["scene-path"].bindCell = OnTreeViewBindCellScenePath; _treeView.columns["field-path"].bindCell = OnTreeViewBindCellFieldPath; _treeView.columns["severity"].unbindCell = OnTreeViewUnbindCellSeverity; _treeView.columns["asset"].comparison = OnTreeViewComparisonCellAsset; _treeView.columns["type"].comparison = OnTreeViewComparisonCellType; _treeView.columns["severity"].comparison = OnTreeViewComparisonCellSeverity; _treeView.columns["severity-message"].comparison = OnTreeViewComparisonCellSeverityMessage; _treeView.columns["scene-path"].comparison = OnTreeViewComparisonCellScenePath; _treeView.columns["field-path"].comparison = OnTreeViewComparisonCellFieldPath; _treeView.selectionChanged += OnTreeViewSelectionChanged; Rebuild(); } public void Rebuild() { if (!Report.HasActive) return; _list.Clear(); for (var i = 0; i < Report.Active.Entries.Count; i++) { _list.Add(new TreeViewItemData(i, Report.Active.Entries[i])); } Filter(); } public void Clear() { _list.Clear(); Filter(); } private void Filter() { _treeView.Clear(); if (string.IsNullOrEmpty(_searchFilter)) { _treeView.SetRootItems(_list); } else { _filteredList.Clear(); for (var i = 0; i < _list.Count; i++) { if (_list[i].data.Filter(_searchFilter)) _filteredList.Add(_list[i]); } _treeView.SetRootItems(_filteredList); } _treeView.Rebuild(); } private static VisualElement CreateLabel() { var label = new Label(); label.AddToClassList("tree-view-label"); return label; } private static VisualElement CreateObjectField() { return new ObjectField { objectType = typeof(UnityEngine.Object), allowSceneObjects = true, label = string.Empty }; } private void OnToolbarButtonRunClicked() { if (!ValidatorRunner.Run()) EditorUtility.DisplayDialog("Project Validator", "Failed to run validators, due to either project in play mode or compiling", "Ok"); } private void OnToolbarButtonClearClicked() { ValidatorRunner.Clear(); } private void OnToolbarSearchFieldChanged(ChangeEvent evt) { _searchFilter = evt.newValue; Filter(); } private void OnTreeViewBindCellAsset(VisualElement ve, int index) { var objectField = (ObjectField)ve; var entry = _treeView.GetItemDataForIndex(index); if (!entry.AssetGuid.Empty()) { objectField.value = AssetDatabase.LoadAssetByGUID(entry.AssetGuid, typeof(UnityEngine.Object)); objectField.style.display = DisplayStyle.Flex; } else { objectField.style.display = DisplayStyle.None; } } private void OnTreeViewBindCellType(VisualElement ve, int index) { var label = (Label)ve; var entry = _treeView.GetItemDataForIndex(index); label.text = entry.Type; } private void OnTreeViewBindCellSeverity(VisualElement ve, int index) { var label = (Label)ve; var entry = _treeView.GetItemDataForIndex(index); label.text = entry.SeverityStr; label.AddToClassList(entry.SeverityStr.ToLower()); } private void OnTreeViewBindCellSeverityMessage(VisualElement ve, int index) { var label = (Label)ve; var entry = _treeView.GetItemDataForIndex(index); label.text = entry.SeverityResult; } private void OnTreeViewBindCellScenePath(VisualElement ve, int index) { var label = (Label)ve; var entry = _treeView.GetItemDataForIndex(index); label.text = entry.ScenePathRichText; } private void OnTreeViewBindCellFieldPath(VisualElement ve, int index) { var label = (Label)ve; var entry = _treeView.GetItemDataForIndex(index); label.text = entry.FieldPathRichText; } private void OnTreeViewUnbindCellSeverity(VisualElement ve, int index) { var label = (Label)ve; label.RemoveFromClassList("error"); label.RemoveFromClassList("warning"); } private int OnTreeViewComparisonCellAsset(int index0, int index1) { var entry0 = _treeView.GetItemDataForIndex(index0); var entry1 = _treeView.GetItemDataForIndex(index1); var assetName0 = EditorAssetUtility.GetAssetName(entry0.AssetGuid); var assetName1 = EditorAssetUtility.GetAssetName(entry1.AssetGuid); return string.Compare(assetName0, assetName1, StringComparison.Ordinal); } private int OnTreeViewComparisonCellType(int index0, int index1) { var entry0 = _treeView.GetItemDataForIndex(index0); var entry1 = _treeView.GetItemDataForIndex(index1); return string.Compare(entry0.Type, entry1.Type, StringComparison.Ordinal); } private int OnTreeViewComparisonCellSeverity(int index0, int index1) { var entry0 = _treeView.GetItemDataForIndex(index0); var entry1 = _treeView.GetItemDataForIndex(index1); return entry0.Severity.CompareTo(entry1.Severity); } private int OnTreeViewComparisonCellSeverityMessage(int index0, int index1) { var entry0 = _treeView.GetItemDataForIndex(index0); var entry1 = _treeView.GetItemDataForIndex(index1); return string.Compare(entry0.SeverityResult, entry1.SeverityResult, StringComparison.Ordinal); } private int OnTreeViewComparisonCellScenePath(int index0, int index1) { var entry0 = _treeView.GetItemDataForIndex(index0); var entry1 = _treeView.GetItemDataForIndex(index1); return string.Compare(entry0.ScenePath, entry1.ScenePath, StringComparison.Ordinal); } private int OnTreeViewComparisonCellFieldPath(int index0, int index1) { var entry0 = _treeView.GetItemDataForIndex(index0); var entry1 = _treeView.GetItemDataForIndex(index1); return string.Compare(entry0.FieldPath, entry1.FieldPath, StringComparison.Ordinal); } private void OnTreeViewSelectionChanged(IEnumerable selection) { foreach (var obj in selection) { if (obj is Report.Entry entry) ProjectValidatorUtility.PingObject(entry); } } } }