Skip to content
This repository has been archived by the owner on Jan 18, 2022. It is now read-only.

Commit

Permalink
Add list support to Worker Inspector (#1396)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jamie Brynes authored Jun 17, 2020
1 parent 8adf7d4 commit 352b85a
Show file tree
Hide file tree
Showing 15 changed files with 479 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,18 @@ public string ToFieldDeclaration(UnityFieldDetails fieldDetails)
var element = optionFieldType.IsNullable ? "NullableVisualElement" : "OptionVisualElement";

return $"private readonly {element}<{innerUiType}, {optionFieldType.ContainedType.FqnType}> {fieldDetails.CamelCaseName}Field;";
case ListFieldType listFieldType:
var innerListType = GetUiFieldType(listFieldType.ContainedType);

if (innerListType == "")
{
// TODO: Eliminate this case by supporting 'Entity'.
return "";
}

return $"private readonly PaginatedListView<{innerListType}, {listFieldType.ContainedType.FqnType}> {fieldDetails.CamelCaseName}Field;";
default:
// TODO: Lists and maps.
// TODO: Maps.
return "";
}
}
Expand Down Expand Up @@ -105,8 +115,40 @@ public IEnumerable<string> ToFieldInitialisation(UnityFieldDetails fieldDetails,
$"{fieldDetails.CamelCaseName}Field = new {element}<{innerUiType}, {optionFieldType.ContainedType.FqnType}>(\"{Formatting.SnakeCaseToHumanReadable(fieldDetails.Name)}\", {fieldDetails.CamelCaseName}InnerField, (element, data) => {{ {ContainedTypeToUiFieldUpdate(optionFieldType.ContainedType, "element", "data")} }});";
yield return $"{parentContainer}.Add({fieldDetails.CamelCaseName}Field);";
break;
case ListFieldType listFieldType:
var innerListType = GetUiFieldType(listFieldType.ContainedType);

if (innerListType == "")
{
// TODO: Eliminate this case by supporting 'Entity'.
yield break;
}

yield return
$"{fieldDetails.CamelCaseName}Field = new PaginatedListView<{innerListType}, {listFieldType.ContainedType.FqnType}>(\"{Formatting.SnakeCaseToHumanReadable(fieldDetails.Name)}\", () => {{ var inner = new {innerListType}(\"\");";

// These lines are part of the func to create an inner list item.
if (listFieldType.ContainedType.Category != ValueType.Type)
{
yield return "inner.SetEnabled(false);";
}

if (listFieldType.ContainedType.Category == ValueType.Enum)
{
yield return $"inner.Init(default({listFieldType.ContainedType.FqnType}));";
}

yield return "return inner; }, (index, data, element) => {";

// These lines are part of the binding.
var labelBinding = listFieldType.ContainedType.Category == ValueType.Type ? "Label" : "label";
yield return $"element.{labelBinding} = $\"Item {{index + 1}}\";";
yield return ContainedTypeToUiFieldUpdate(listFieldType.ContainedType, "element", "data");
yield return "});";
yield return $"{parentContainer}.Add({fieldDetails.CamelCaseName}Field);";
break;
default:
// TODO: Lists and maps.
// TODO: Maps.
yield break;
}
}
Expand All @@ -125,9 +167,17 @@ public string ToUiFieldUpdate(UnityFieldDetails fieldDetails, string fieldParent
return "";
}

return $"{uiElementName}.Update({fieldParent}.{fieldDetails.PascalCaseName});";
case ListFieldType listFieldType:
if (GetUiFieldType(listFieldType.ContainedType) == "")
{
// TODO: Eliminate this case by supporting 'Entity'.
return "";
}

return $"{uiElementName}.Update({fieldParent}.{fieldDetails.PascalCaseName});";
default:
// TODO: Lists and maps.
// TODO: Maps.
return "";
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Improbable.Gdk.Debug.WorkerInspector.Codegen.EditmodeTests")]

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

namespace Improbable.Gdk.Debug.WorkerInspector.Codegen
{
public class PaginatedListView<TElement, TData> : VisualElement
where TElement : VisualElement
{
private const string UxmlPath =
"Packages/io.improbable.gdk.debug/WorkerInspector/Templates/PaginatedListView.uxml";

private List<TData> data;

private readonly Action<int, TData, TElement> bindElement;
private readonly VisualElement container;
private readonly ElementPool<TElement> elementPool;
private readonly int elementsPerPage;

private readonly VisualElement controlsContainer;
private readonly Button forwardButton;
private readonly Button backButton;
private readonly Label pageCounter;

private int currentPage = 0;
private int numPages = 0;

public PaginatedListView(string label, Func<TElement> makeElement, Action<int, TData, TElement> bindElement, int elementsPerPage = 5)
{
this.bindElement = bindElement;
this.elementsPerPage = elementsPerPage;

var template = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(UxmlPath);
template.CloneTree(this);

this.Q<Label>(name: "list-name").text = label;
container = this.Q<VisualElement>(className: "user-defined-type-container-data");

controlsContainer = this.Q<VisualElement>(className: "paginated-list-controls");
pageCounter = this.Q<Label>(name: "page-counter");

backButton = this.Q<Button>(name: "back-button");
backButton.clickable.clicked += () => ChangePageCount(-1);

forwardButton = this.Q<Button>(name: "forward-button");
forwardButton.clickable.clicked += () => ChangePageCount(1);

elementPool = new ElementPool<TElement>(makeElement);
}

public void Update(List<TData> newData)
{
data = newData;

if (data.Count == 0)
{
controlsContainer.AddToClassList("hidden");
}
else
{
controlsContainer.RemoveFromClassList("hidden");
}

CalculatePages();
RefreshView();
}

internal void ChangePageCount(int diff)
{
currentPage += diff;
currentPage = Mathf.Clamp(currentPage, 0, numPages - 1);
CalculatePages();
RefreshView();
}

private void CalculatePages()
{
numPages = Mathf.CeilToInt((float) data.Count / elementsPerPage);
numPages = Mathf.Clamp(numPages, 1, numPages);
currentPage = Mathf.Clamp(currentPage, 0, numPages - 1);

pageCounter.text = $"{currentPage + 1}/{numPages}";
}

private void RefreshView()
{
// Calculate slice of list to be rendered.
var firstIndex = currentPage * elementsPerPage;
var length = Math.Min(elementsPerPage, data.Count - firstIndex);

// If the child count is the same, don't adjust it.
// If the child count is less, add the requisite number.
// If the child count is more, pop elements off the end.

var diff = container.childCount - length;

if (diff > 0)
{
for (var i = 0; i < diff; i++)
{
var element = container.ElementAt(container.childCount - 1);
container.RemoveAt(container.childCount - 1);
elementPool.Return((TElement) element);
}
}
else if (diff < 0)
{
for (var i = diff; i < 0; i++)
{
container.Add(elementPool.GetOrCreate());
}
}

// At this point, container.Children() has the same length as the slice.
var elementIndex = firstIndex;
foreach (var child in container.Children())
{
bindElement(elementIndex, data[elementIndex], (TElement) child);
elementIndex++;
}

backButton.SetEnabled(currentPage != 0);
forwardButton.SetEnabled(currentPage != numPages - 1);
}
}

internal class ElementPool<TElement> where TElement : VisualElement
{
private readonly Stack<TElement> pool = new Stack<TElement>();
private readonly Func<TElement> makeElement;

public ElementPool(Func<TElement> makeElement)
{
this.makeElement = makeElement;
}

public TElement GetOrCreate()
{
return pool.Count == 0 ? makeElement() : pool.Pop();
}

public void Return(TElement element)
{
pool.Push(element);
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,21 @@ namespace Improbable.Gdk.Debug.WorkerInspector.Codegen
{
public abstract class SchemaTypeVisualElement<T> : VisualElement
{
public string Label
{
get => labelElement.text;
set => labelElement.text = value;
}

protected readonly VisualElement Container;
private readonly Label labelElement;

protected SchemaTypeVisualElement(string label)
{
AddToClassList("user-defined-type-container");

Add(new Label(label));
labelElement = new Label(label);
Add(labelElement);

Container = new VisualElement();
Container.AddToClassList("user-defined-type-container-data");
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "Improbable.Gdk.Debug.WorkerInspector.Codegen.EditmodeTests",
"references": [
"Improbable.Gdk.Debug.WorkerInspector.Codegen"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 352b85a

Please sign in to comment.