Added initial version 0.1.0

This commit is contained in:
Anders Ejlersen 2026-05-17 22:12:04 +02:00
parent 6aa4cb8596
commit 416759c213
64 changed files with 2181 additions and 0 deletions

View file

@ -0,0 +1,247 @@
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<TreeViewItemData<Report.Entry>> _list = new();
private readonly List<TreeViewItemData<Report.Entry>> _filteredList = new();
public void CreateGUI()
{
var root = rootVisualElement;
var asset = EditorAssetUtility.LoadFirstAsset<VisualTreeAsset>("UxmlEditorProjectValidatorWindow");
root.Add(asset.Instantiate());
root.Q<ToolbarButton>("button-run").clicked += OnToolbarButtonRunClicked;
root.Q<ToolbarButton>("button-clear").clicked += OnToolbarButtonClearClicked;
root.Q<ToolbarSearchField>().RegisterValueChangedCallback(OnToolbarSearchFieldChanged);
_treeView = root.Q<MultiColumnTreeView>();
_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<Report.Entry>(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<string> evt)
{
_searchFilter = evt.newValue;
Filter();
}
private void OnTreeViewBindCellAsset(VisualElement ve, int index)
{
var objectField = (ObjectField)ve;
var entry = _treeView.GetItemDataForIndex<Report.Entry>(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<Report.Entry>(index);
label.text = entry.Type;
}
private void OnTreeViewBindCellSeverity(VisualElement ve, int index)
{
var label = (Label)ve;
var entry = _treeView.GetItemDataForIndex<Report.Entry>(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<Report.Entry>(index);
label.text = entry.SeverityResult;
}
private void OnTreeViewBindCellScenePath(VisualElement ve, int index)
{
var label = (Label)ve;
var entry = _treeView.GetItemDataForIndex<Report.Entry>(index);
label.text = entry.ScenePathRichText;
}
private void OnTreeViewBindCellFieldPath(VisualElement ve, int index)
{
var label = (Label)ve;
var entry = _treeView.GetItemDataForIndex<Report.Entry>(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<Report.Entry>(index0);
var entry1 = _treeView.GetItemDataForIndex<Report.Entry>(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<Report.Entry>(index0);
var entry1 = _treeView.GetItemDataForIndex<Report.Entry>(index1);
return string.Compare(entry0.Type, entry1.Type, StringComparison.Ordinal);
}
private int OnTreeViewComparisonCellSeverity(int index0, int index1)
{
var entry0 = _treeView.GetItemDataForIndex<Report.Entry>(index0);
var entry1 = _treeView.GetItemDataForIndex<Report.Entry>(index1);
return entry0.Severity.CompareTo(entry1.Severity);
}
private int OnTreeViewComparisonCellSeverityMessage(int index0, int index1)
{
var entry0 = _treeView.GetItemDataForIndex<Report.Entry>(index0);
var entry1 = _treeView.GetItemDataForIndex<Report.Entry>(index1);
return string.Compare(entry0.SeverityResult, entry1.SeverityResult, StringComparison.Ordinal);
}
private int OnTreeViewComparisonCellScenePath(int index0, int index1)
{
var entry0 = _treeView.GetItemDataForIndex<Report.Entry>(index0);
var entry1 = _treeView.GetItemDataForIndex<Report.Entry>(index1);
return string.Compare(entry0.ScenePath, entry1.ScenePath, StringComparison.Ordinal);
}
private int OnTreeViewComparisonCellFieldPath(int index0, int index1)
{
var entry0 = _treeView.GetItemDataForIndex<Report.Entry>(index0);
var entry1 = _treeView.GetItemDataForIndex<Report.Entry>(index1);
return string.Compare(entry0.FieldPath, entry1.FieldPath, StringComparison.Ordinal);
}
private void OnTreeViewSelectionChanged(IEnumerable<object> selection)
{
foreach (var obj in selection)
{
if (obj is Report.Entry entry)
ProjectValidatorUtility.PingObject(entry);
}
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6abd87229d8145e287c557745821486e
timeCreated: 1778924130

8
Editor/Window/Uxml.meta Normal file
View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8fe33b189cbbf5d4c9ae0162be9cd719
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,19 @@
.tree-view-label {
-unity-text-align: middle-left;
flex-grow: 1;
text-overflow: ellipsis;
white-space: nowrap;
padding-right: 6px;
padding-left: 6px;
}
.warning {
color: rgb(255, 165, 0);
}
.error {
color: rgb(255, 0, 0);
-unity-font-style: bold;
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 360a611fbc24ba94895f4251f434f40b
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
disableValidation: 0
unsupportedSelectorAction: 0

View file

@ -0,0 +1,3 @@
.unity-list-view__item Label {
display: none;
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: afcc2218364545745bbf480be6415eef
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
disableValidation: 0
unsupportedSelectorAction: 0

View file

@ -0,0 +1,20 @@
<ui:UXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" noNamespaceSchemaLocation="../../../../../../UIElementsSchema/UIElements.xsd" editor-extension-mode="True">
<Style src="project://database/Assets/ProjectValidator/Editor/Window/Uxml/StyleSheetEditorProjectValidatorWindow.uss?fileID=7433441132597879392&amp;guid=360a611fbc24ba94895f4251f434f40b&amp;type=3#StyleSheetEditorProjectValidatorWindow"/>
<uie:Toolbar name="toolbar">
<uie:ToolbarButton text="Run" name="button-run" style="margin-left: 2px;"/>
<uie:ToolbarButton text="Clear" name="button-clear"/>
<uie:ToolbarSpacer style="flex-grow: 1;"/>
<uie:ToolbarSearchField name="search-field"/>
</uie:Toolbar>
<ui:MultiColumnTreeView columns="" sort-column-descriptions="" name="tree-view" show-alternating-row-backgrounds="All" data-source-type="Module.ProjectValidator.Editor.Report+Entry, Module.ProjectValidator.Editor" sorting-mode="Default" class="tree-view">
<ui:Columns reorderable="false" primary-column-name="severity">
<ui:Column name="severity" title="Severity" min-width="80px" optional="true"/>
<ui:Column name="type" title="Type" min-width="42px" width="120px"/>
<ui:Column name="asset" title="Asset" optional="true" min-width="80px" width="200px"/>
<ui:Column optional="true" name="scene-path" title="Scene Path" min-width="40px" width="200px"/>
<ui:Column name="field-path" title="Field Path" min-width="70px" width="200px"/>
<ui:Column optional="true" name="severity-message" title="Message" stretchable="true"/>
</ui:Columns>
<ui:SortColumnDescriptions/>
</ui:MultiColumnTreeView>
</ui:UXML>

View file

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: fb7069498efc1d44084439b9aa291bcc
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}