Skip to content

Commit

Permalink
ui(error): add avatar selection UI to error report window
Browse files Browse the repository at this point in the history
  • Loading branch information
bdunderscore committed Dec 20, 2023
1 parent c48b20c commit 1a800bf
Show file tree
Hide file tree
Showing 5 changed files with 275 additions and 20 deletions.
178 changes: 161 additions & 17 deletions Editor/ErrorReporting/UI/ErrorReportWindow.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
#region

using System;
using System.Collections.Generic;
using System.Linq;
using nadena.dev.ndmf.localization;
using nadena.dev.ndmf.runtime;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UIElements;
using Object = UnityEngine.Object;

#endregion

Expand All @@ -21,19 +26,57 @@ public class ErrorReportWindow : EditorWindow
{
private Label _avatarHeader;

private VisualElement _errorList, _noErrorLabel;
private VisualElement _errorList, _noErrorLabel, _unbuiltContainer, _noAvatarLabel;
private ErrorReport _report;
private Button _testBuild;

[SerializeField] // retain over domain reloads
private GameObject _avatarRoot;

private List<Button> _testBuild;

#if UNITY_2021_3_OR_NEWER
private ToolbarMenu _selector;
#endif

public ErrorReport CurrentReport
{
get => _report;
set
{
if (_report == value) return;

_report = value;
if (_report?.TryResolveAvatar(out _avatarRoot) != true)
{
_avatarRoot = null;
}
if (_errorList != null)
{
UpdateErrorList();
UpdateContents();
}
}
}

public GameObject CurrentAvatar
{
get => _avatarRoot;
set
{
if (_avatarRoot == value) return;

var avatarPath = RuntimeUtil.RelativePath(null, value);
var report = ErrorReport.Reports.FirstOrDefault(r => r.AvatarRootPath == avatarPath);

if (report == null)
{
_report = null;
_avatarRoot = value;
UpdateContents();
}
else
{
CurrentReport = report;
_avatarRoot = value;
}
}
}
Expand All @@ -58,14 +101,73 @@ public void CreateGUI()

NDMFLocales.L.LocalizeUIElements(root);

var errorList = root.Q<VisualElement>("error-list");
var errorList = root.Q<VisualElement>("error-list-container");

_errorList = errorList;
_avatarHeader = root.Q<Label>("avatar-header-placeholder-label");
_testBuild = root.Q<Button>("test-build-button");
_testBuild.clicked += TestBuild;
_testBuild.SetEnabled(_report != null && _report.TryResolveAvatar(out _));
_noErrorLabel = root.Q<VisualElement>("no-errors-label");
_unbuiltContainer = root.Q<VisualElement>("unbuilt-container");
_noAvatarLabel = root.Q<VisualElement>("no-avatar-label");

_testBuild = root.Query<Button>(className: "test-build-button").ToList();
foreach (var button in _testBuild)
{
button.clicked += TestBuild;
button.SetEnabled(false);
}

SetupSelector();
EditorApplication.hierarchyChanged += SetupSelector;

UpdateContents();
}

private void OnEnable()
{
if (_testBuild != null)
{
// GUI setup done
EditorApplication.hierarchyChanged += SetupSelector;
}
}

private void OnDisable()
{
EditorApplication.hierarchyChanged -= SetupSelector;
}

private void SetupSelector()
{
var container = rootVisualElement.Q<VisualElement>("avatar-selector-container");

#if UNITY_2021_3_OR_NEWER

if (_selector != null)
{
container.Remove(_selector);
}

_selector = new ToolbarMenu();
container.Add(_selector);

_selector.text = _avatarRoot != null ? _avatarRoot.name : (_report != null ? _report.AvatarName : "<???>");

foreach (var root in RuntimeUtil.FindAvatarRoots())
{
Debug.Log(root.name);
_selector.menu.AppendAction(root.name, _ =>
{
CurrentAvatar = root;
});
}
#else
var container = rootVisualElement.Q<VisualElement>("avatar-selector-container");
container.style.display = DisplayStyle.None;

var placeholder = rootVisualElement.Q<VisualElement>("avatar-header-placeholder-label");
placeholder.style.display = DisplayStyle.Flex;
placeholder.text = "Avatar: " + (_report.AvatarName ?? _avatarRoot.name ?? "<???>");
#endif
}

[MenuItem("Tools/NDM Framework/Show Error Report")]
Expand All @@ -78,13 +180,9 @@ public static void ShowErrorReportWindow()

private void TestBuild()
{
Debug.Log("TestBuild");
if (_report == null) return;

if (!_report.TryResolveAvatar(out var originalRoot)) return;
Debug.Log("Report OK, root=" + originalRoot);

var clone = Instantiate(originalRoot);
if (_avatarRoot == null) return;

var clone = Instantiate(_avatarRoot);

try
{
Expand All @@ -101,12 +199,25 @@ private void TestBuild()
}
}

void UpdateErrorList()
void UpdateContents()
{
_errorList.Clear();

foreach (var button in _testBuild)
{
button.SetEnabled(_avatarRoot != null);
}

_unbuiltContainer.style.display = DisplayStyle.None;
_errorList.style.display = DisplayStyle.None;
_noErrorLabel.style.display = DisplayStyle.None;
_noAvatarLabel.style.display = DisplayStyle.None;

if (_report != null)
{
_errorList.style.display = DisplayStyle.Flex;
_errorList.Clear();

var errors = _report.Errors.OrderBy(e => e.Plugin.DisplayName).ToList();
PluginBase lastPlugin = null;

Expand All @@ -125,12 +236,21 @@ void UpdateErrorList()
}

_avatarHeader.text = "Avatar: " + _report.AvatarName;

_testBuild.SetEnabled(FindAvatarRoot(_report) != null);

_noErrorLabel.style.display = _report.Errors.Count == 0 ?
DisplayStyle.Flex : DisplayStyle.None;
}
else if (_avatarRoot != null)
{
_unbuiltContainer.style.display = DisplayStyle.Flex;
_avatarHeader.text = "Avatar: " + _avatarRoot.name;
}
else
{
_noAvatarLabel.style.display = DisplayStyle.Flex;
}

SetupSelector();
}

private GameObject FindAvatarRoot(ErrorReport report)
Expand Down Expand Up @@ -166,6 +286,30 @@ public static void ShowReport(ErrorReport report)
wnd.CurrentReport = report;
wnd.Show();
}

public static void ShowReport(GameObject avatarRoot)
{
if (Application.isBatchMode || avatarRoot == null) return;

ErrorReportWindow wnd = GetWindow<ErrorReportWindow>();
wnd.titleContent = new GUIContent("NDMF Error Report");
wnd.CurrentAvatar = avatarRoot;
wnd.Show();
}

[MenuItem("GameObject/NDM Framework/Show Error Report", false)]
private static void ShowCurrentAvatarErrorReport()
{
if (Selection.activeGameObject == null) return;

ShowReport(Selection.activeGameObject);
}

[MenuItem("GameObject/NDM Framework/Show Error Report", true)]
private static bool ShowCurrentAvatarErrorReportValidation()
{
return Selection.activeGameObject != null && RuntimeUtil.IsAvatarRoot(Selection.activeGameObject.transform);
}
}

internal class TestError : SimpleError
Expand Down
47 changes: 47 additions & 0 deletions Editor/ErrorReporting/UI/Resources/ErrorReportWindow.uss
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,51 @@ VisualElement {
align-self: center;
margin-top: 10px;
font-size: 150%;
}

#no-errors-label {
display: none;
}

#unbuilt-container {
display: none;
}

#error-list-container {
display: none;
}

#avatar-header-placeholder-label {
display: none;
}

#avatar-selector-container {
display: flex;
flex-direction: row;
}

#unbuilt-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex-grow: 1;
}

#unbuilt-container Label {
flex-grow: 0;
font-size: 20px;
margin-top: 20px;
}

#unbuilt-container Button {
font-size: 30px;
margin-top: 10px;
padding: 15px;
padding-left: 20px;
padding-right: 20px;
}

#test-build-button {
padding: 3px;
}
14 changes: 12 additions & 2 deletions Editor/ErrorReporting/UI/Resources/ErrorReportWindow.uxml
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,26 @@
</engine:VisualElement>

<engine:VisualElement name="avatarHeader">
<engine:Label text="Avatar: ???" class="ndmf-tr" name="avatar-header-placeholder-label"/>
<!-- TODO translate -->
<engine:Label text="Avatar: ???" name="avatar-header-placeholder-label"/>
<engine:VisualElement name="avatar-selector-container">
<engine:Label text="ErrorReport:AvatarPrefix" class="ndmf-tr"/>
</engine:VisualElement>
</engine:VisualElement>

<engine:ScrollView name="error-list" show-vertical-scroller="true" show-horizontal-scroller="false">
<engine:Label text="ErrorReport:NoAvatarSelected" class="ndmf-tr" name="no-avatar-label"/>
<engine:Label text="ErrorReport:NoErrors" class="ndmf-tr" name="no-errors-label"/>
<engine:VisualElement name="unbuilt-container">
<engine:Label text="ErrorReport:Unbuilt" class="ndmf-tr"/>
<engine:Button text="ErrorReport:TestBuild" class="ndmf-tr test-build-button big-button"/>
</engine:VisualElement>
<engine:VisualElement name="error-list-container"/>
</engine:ScrollView>

<engine:VisualElement name="footer">
<ndmf:LanguageSwitcher/>
<engine:Button text="ErrorReport:TestBuild" name="test-build-button" class="ndmf-tr"/>
<engine:Button text="ErrorReport:TestBuild" name="test-build-button" class="ndmf-tr test-build-button"/>
</engine:VisualElement>

</engine:UXML>
11 changes: 10 additions & 1 deletion Editor/UI/Localization/en-US.po
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,27 @@ msgid ""
msgstr ""
"Language: en-US\n"

msgid "ErrorReport:AvatarPrefix"
msgstr "Avatar: "

msgid "ErrorReport:Title"
msgstr "Error Report"

msgid "ErrorReport:HintFoldout"
msgstr "Help me fix it!"

msgid "ErrorReport:TestBuild"
msgstr "Test Build"
msgstr "Perform Test Build"

msgid "ErrorReport:NoErrors"
msgstr "No errors to report!"

msgid "ErrorReport:NoAvatarSelected"
msgstr "No avatar selected."

msgid "ErrorReport:Unbuilt"
msgstr "This avatar has not been built yet."

msgid "Errors:InternalError"
msgstr "An internal error has occurred."

Expand Down
Loading

0 comments on commit 1a800bf

Please sign in to comment.