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

Commit 0dde9a1

Browse files
author
Jamie Brynes
authored
Add Option<T> rendering to the Worker Inspector (#1392)
1 parent d60046b commit 0dde9a1

File tree

6 files changed

+221
-61
lines changed

6 files changed

+221
-61
lines changed

workers/unity/Packages/io.improbable.gdk.debug/.codegen/Source/Generators/DebugExtensions/FieldTypeHandler.cs

+93-42
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,20 @@ public string ToFieldDeclaration(UnityFieldDetails fieldDetails)
3131
}
3232

3333
return $"private readonly {uiType} {fieldDetails.CamelCaseName}Field;";
34+
case OptionFieldType optionFieldType:
35+
var innerUiType = GetUiFieldType(optionFieldType.ContainedType);
36+
37+
if (innerUiType == "")
38+
{
39+
// TODO: Eliminate this case by supporting 'Entity'.
40+
return "";
41+
}
42+
43+
var element = optionFieldType.IsNullable ? "NullableVisualElement" : "OptionVisualElement";
44+
45+
return $"private readonly {element}<{innerUiType}, {optionFieldType.ContainedType.FqnType}> {fieldDetails.CamelCaseName}Field;";
3446
default:
35-
// TODO: Lists, maps, and options
47+
// TODO: Lists and maps.
3648
return "";
3749
}
3850
}
@@ -66,8 +78,35 @@ public IEnumerable<string> ToFieldInitialisation(UnityFieldDetails fieldDetails,
6678

6779
yield return $"{parentContainer}.Add({fieldDetails.CamelCaseName}Field);";
6880
break;
81+
case OptionFieldType optionFieldType:
82+
var innerUiType = GetUiFieldType(optionFieldType.ContainedType);
83+
84+
if (innerUiType == "")
85+
{
86+
// TODO: Eliminate this case by supporting 'Entity'.
87+
yield break;
88+
}
89+
90+
yield return $"var {fieldDetails.CamelCaseName}InnerField = new {innerUiType}(\"Value\");";
91+
92+
if (optionFieldType.ContainedType.Category != ValueType.Type)
93+
{
94+
yield return $"{fieldDetails.CamelCaseName}InnerField.SetEnabled(false);";
95+
}
96+
97+
if (optionFieldType.ContainedType.Category == ValueType.Enum)
98+
{
99+
yield return $"{fieldDetails.CamelCaseName}InnerField.Init(default({optionFieldType.ContainedType.FqnType}));";
100+
}
101+
102+
var element = optionFieldType.IsNullable ? "NullableVisualElement" : "OptionVisualElement";
103+
104+
yield return
105+
$"{fieldDetails.CamelCaseName}Field = new {element}<{innerUiType}, {optionFieldType.ContainedType.FqnType}>(\"{Formatting.SnakeCaseToHumanReadable(fieldDetails.Name)}\", {fieldDetails.CamelCaseName}InnerField, (element, data) => {{ {ContainedTypeToUiFieldUpdate(optionFieldType.ContainedType, "element", "data")} }});";
106+
yield return $"{parentContainer}.Add({fieldDetails.CamelCaseName}Field);";
107+
break;
69108
default:
70-
// TODO: Lists, maps, and options
109+
// TODO: Lists and maps.
71110
yield break;
72111
}
73112
}
@@ -78,51 +117,63 @@ public string ToUiFieldUpdate(UnityFieldDetails fieldDetails, string fieldParent
78117
switch (fieldDetails.FieldType)
79118
{
80119
case SingularFieldType singularFieldType:
81-
switch (singularFieldType.ContainedType.Category)
120+
return ContainedTypeToUiFieldUpdate(singularFieldType.ContainedType, uiElementName, $"{fieldParent}.{fieldDetails.PascalCaseName}");
121+
case OptionFieldType optionFieldType:
122+
if (GetUiFieldType(optionFieldType.ContainedType) == "")
82123
{
83-
case ValueType.Enum:
84-
return $"{uiElementName}.value = {fieldParent}.{fieldDetails.PascalCaseName};";
85-
case ValueType.Primitive:
86-
var primitiveType = singularFieldType.ContainedType.PrimitiveType.Value;
87-
88-
switch (primitiveType)
89-
{
90-
case PrimitiveType.Int32:
91-
case PrimitiveType.Int64:
92-
case PrimitiveType.Uint32:
93-
case PrimitiveType.Uint64:
94-
case PrimitiveType.Sint32:
95-
case PrimitiveType.Sint64:
96-
case PrimitiveType.Fixed32:
97-
case PrimitiveType.Fixed64:
98-
case PrimitiveType.Sfixed32:
99-
case PrimitiveType.Sfixed64:
100-
case PrimitiveType.Float:
101-
case PrimitiveType.Double:
102-
case PrimitiveType.String:
103-
case PrimitiveType.EntityId:
104-
return $"{uiElementName}.value = {fieldParent}.{fieldDetails.PascalCaseName}.ToString();";
105-
case PrimitiveType.Bytes:
106-
return $"{uiElementName}.value = global::System.Text.Encoding.Default.GetString({fieldParent}.{fieldDetails.PascalCaseName});";
107-
case PrimitiveType.Bool:
108-
return $"{uiElementName}.value = {fieldParent}.{fieldDetails.PascalCaseName};";
109-
break;
110-
case PrimitiveType.Entity:
111-
// TODO: Entity type.
112-
return "";
113-
case PrimitiveType.Invalid:
114-
throw new ArgumentException("Unknown primitive type encountered");
115-
default:
116-
throw new ArgumentOutOfRangeException();
117-
}
118-
case ValueType.Type:
119-
return $"{uiElementName}.Update({fieldParent}.{fieldDetails.PascalCaseName});";
124+
// TODO: Eliminate this case by supporting 'Entity'.
125+
return "";
126+
}
127+
128+
return $"{uiElementName}.Update({fieldParent}.{fieldDetails.PascalCaseName});";
129+
default:
130+
// TODO: Lists and maps.
131+
return "";
132+
}
133+
}
134+
135+
private string ContainedTypeToUiFieldUpdate(ContainedType containedType, string uiElementName, string fieldAccessor)
136+
{
137+
switch (containedType.Category)
138+
{
139+
case ValueType.Enum:
140+
return $"{uiElementName}.value = {fieldAccessor};";
141+
case ValueType.Primitive:
142+
var primitiveType = containedType.PrimitiveType.Value;
143+
switch (primitiveType)
144+
{
145+
case PrimitiveType.Int32:
146+
case PrimitiveType.Int64:
147+
case PrimitiveType.Uint32:
148+
case PrimitiveType.Uint64:
149+
case PrimitiveType.Sint32:
150+
case PrimitiveType.Sint64:
151+
case PrimitiveType.Fixed32:
152+
case PrimitiveType.Fixed64:
153+
case PrimitiveType.Sfixed32:
154+
case PrimitiveType.Sfixed64:
155+
case PrimitiveType.Float:
156+
case PrimitiveType.Double:
157+
case PrimitiveType.String:
158+
case PrimitiveType.EntityId:
159+
return $"{uiElementName}.value = {fieldAccessor}.ToString();";
160+
case PrimitiveType.Bytes:
161+
return $"{uiElementName}.value = global::System.Text.Encoding.Default.GetString({fieldAccessor});";
162+
case PrimitiveType.Bool:
163+
return $"{uiElementName}.value = {fieldAccessor};";
164+
break;
165+
case PrimitiveType.Entity:
166+
// TODO: Entity type.
167+
return "";
168+
case PrimitiveType.Invalid:
169+
throw new ArgumentException("Unknown primitive type encountered");
120170
default:
121171
throw new ArgumentOutOfRangeException();
122172
}
173+
case ValueType.Type:
174+
return $"{uiElementName}.Update({fieldAccessor});";
123175
default:
124-
// TODO: Lists, maps, and options
125-
return "";
176+
throw new ArgumentOutOfRangeException();
126177
}
127178
}
128179

workers/unity/Packages/io.improbable.gdk.debug/WorkerInspector/Codegen/Improbable.Gdk.Debug.WorkerInspector.Codegen.asmdef

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{
22
"name": "Improbable.Gdk.Debug.WorkerInspector.Codegen",
33
"references": [
4-
"Unity.Entities"
4+
"Unity.Entities",
5+
"Improbable.Gdk.Core"
56
],
67
"includePlatforms": [
78
"Editor"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
using System;
2+
using Improbable.Gdk.Core;
3+
using UnityEngine.UIElements;
4+
5+
namespace Improbable.Gdk.Debug.WorkerInspector.Codegen
6+
{
7+
public sealed class OptionVisualElement<TElement, TData> : OptionalVisualElementBase<TElement, TData>
8+
where TElement : VisualElement
9+
{
10+
public OptionVisualElement(string label, TElement innerElement, Action<TElement, TData> applyData) : base(label, innerElement, applyData)
11+
{
12+
}
13+
14+
public void Update(Option<TData> data)
15+
{
16+
if (data.HasValue)
17+
{
18+
UpdateWithData(data.Value);
19+
}
20+
else
21+
{
22+
UpdateWithoutData();
23+
}
24+
}
25+
}
26+
27+
public sealed class NullableVisualElement<TElement, TData> : OptionalVisualElementBase<TElement, TData>
28+
where TData : struct
29+
where TElement : VisualElement
30+
{
31+
public NullableVisualElement(string label, TElement innerElement, Action<TElement, TData> applyData) : base(label, innerElement, applyData)
32+
{
33+
}
34+
35+
public void Update(TData? data)
36+
{
37+
if (data.HasValue)
38+
{
39+
UpdateWithData(data.Value);
40+
}
41+
else
42+
{
43+
UpdateWithoutData();
44+
}
45+
}
46+
}
47+
48+
public class OptionalVisualElementBase<TElement, TData> : VisualElement where TElement : VisualElement
49+
{
50+
private readonly VisualElement container;
51+
private readonly TElement innerElement;
52+
private readonly Label isEmptyLabel;
53+
private readonly Action<TElement, TData> applyData;
54+
55+
protected OptionalVisualElementBase(string label, TElement innerElement, Action<TElement, TData> applyData)
56+
{
57+
AddToClassList("user-defined-type-container");
58+
Add(new Label(label));
59+
60+
container = new VisualElement();
61+
container.AddToClassList("user-defined-type-container-data");
62+
Add(container);
63+
64+
isEmptyLabel = new Label("Option is empty.");
65+
isEmptyLabel.AddToClassList("label-empty-option");
66+
67+
this.innerElement = innerElement;
68+
this.applyData = applyData;
69+
}
70+
71+
protected void UpdateWithData(TData data)
72+
{
73+
RemoveIfPresent(isEmptyLabel);
74+
container.Add(innerElement);
75+
76+
applyData(innerElement, data);
77+
}
78+
79+
protected void UpdateWithoutData()
80+
{
81+
RemoveIfPresent(innerElement);
82+
container.Add(isEmptyLabel);
83+
}
84+
85+
private void RemoveIfPresent(VisualElement element)
86+
{
87+
if (container.Contains(element))
88+
{
89+
container.Remove(element);
90+
}
91+
}
92+
}
93+
}

workers/unity/Packages/io.improbable.gdk.debug/WorkerInspector/Codegen/OptionVisualElement.cs.meta

+11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

workers/unity/Packages/io.improbable.gdk.debug/WorkerInspector/Templates/WorkerInspectorWindow.uss

+6
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,9 @@
148148
#worker-details .unity-text-field:disabled, .unity-toggle:disabled {
149149
opacity: 1;
150150
}
151+
152+
.label-empty-option {
153+
margin-left: 3px;
154+
margin-top: 1px;
155+
margin-bottom: 1px;
156+
}

workers/unity/Packages/io.improbable.gdk.tools/.CodeGenTemplate/CodeGenerationLib/Model/Details/FieldTypes/OptionFieldType.cs

+16-18
Original file line numberDiff line numberDiff line change
@@ -9,39 +9,37 @@ public string Type
99
{
1010
get
1111
{
12-
if (containedType.FqnType ==
13-
UnityTypeMappings.SchemaTypeToUnityType[PrimitiveType.String]
14-
||
15-
containedType.FqnType ==
16-
UnityTypeMappings.SchemaTypeToUnityType[PrimitiveType.Bytes])
12+
if (!IsNullable)
1713
{
18-
return $"global::Improbable.Gdk.Core.Option<{containedType.FqnType}>";
14+
return $"global::Improbable.Gdk.Core.Option<{ContainedType.FqnType}>";
1915
}
2016

21-
return $"{containedType.FqnType}?";
17+
return $"{ContainedType.FqnType}?";
2218
}
2319
}
2420

25-
private readonly ContainedType containedType;
21+
public readonly ContainedType ContainedType;
22+
public readonly bool IsNullable;
2623

2724
public OptionFieldType(TypeReference innerType)
2825
{
29-
containedType = new ContainedType(innerType);
26+
ContainedType = new ContainedType(innerType);
27+
IsNullable = ContainedType.FqnType != UnityTypeMappings.SchemaTypeToUnityType[PrimitiveType.String] && ContainedType.FqnType != UnityTypeMappings.SchemaTypeToUnityType[PrimitiveType.Bytes];
3028
}
3129

3230
public string GetSerializationString(string fieldInstance, string schemaObject, uint fieldNumber)
3331
{
3432
return new IfElseBlock($"{fieldInstance}.HasValue", then =>
3533
{
36-
then.Line(containedType.GetSerializationStatement($"{fieldInstance}.Value", schemaObject, fieldNumber));
34+
then.Line(ContainedType.GetSerializationStatement($"{fieldInstance}.Value", schemaObject, fieldNumber));
3735
}).Format();
3836
}
3937

4038
public string GetDeserializationString(string fieldInstance, string schemaObject, uint fieldNumber)
4139
{
42-
return new IfElseBlock($"{containedType.GetCountExpression(schemaObject, fieldNumber)} == 1", then =>
40+
return new IfElseBlock($"{ContainedType.GetCountExpression(schemaObject, fieldNumber)} == 1", then =>
4341
{
44-
then.Line($"{fieldInstance} = new {Type}({containedType.GetDeserializationExpression(schemaObject, fieldNumber)});");
42+
then.Line($"{fieldInstance} = new {Type}({ContainedType.GetDeserializationExpression(schemaObject, fieldNumber)});");
4543
}).Format();
4644
}
4745

@@ -55,11 +53,11 @@ public string GetDeserializeUpdateString(string fieldInstance, string schemaObje
5553
{
5654
then.Line($"{fieldInstance} = new {Type}();");
5755
})
58-
.ElseIf($"{containedType.GetCountExpression(schemaObject, fieldNumber)} == 1", then =>
56+
.ElseIf($"{ContainedType.GetCountExpression(schemaObject, fieldNumber)} == 1", then =>
5957
{
6058
then.Line(new[]
6159
{
62-
$"var value = {containedType.GetDeserializationExpression(schemaObject, fieldNumber)};",
60+
$"var value = {ContainedType.GetDeserializationExpression(schemaObject, fieldNumber)};",
6361
$"{fieldInstance} = new {Type}(value);"
6462
});
6563
});
@@ -76,21 +74,21 @@ public string GetDeserializeUpdateIntoUpdateString(string updateFieldInstance, s
7674
then =>
7775
{
7876
then.Line($"{updateFieldInstance} = new global::Improbable.Gdk.Core.Option<{Type}>(new {Type}());");
79-
}).ElseIf($"{containedType.GetCountExpression(schemaObject, fieldNumber)} == 1",
77+
}).ElseIf($"{ContainedType.GetCountExpression(schemaObject, fieldNumber)} == 1",
8078
then =>
8179
{
82-
then.Line($"{updateFieldInstance} = new global::Improbable.Gdk.Core.Option<{Type}>({containedType.GetDeserializationExpression(schemaObject, fieldNumber)});");
80+
then.Line($"{updateFieldInstance} = new global::Improbable.Gdk.Core.Option<{Type}>({ContainedType.GetDeserializationExpression(schemaObject, fieldNumber)});");
8381
});
8482
}).Format();
8583
}
8684

8785
public string GetDeserializeDataIntoUpdateString(string updateFieldInstance, string schemaObject, uint fieldNumber)
8886
{
89-
return new IfElseBlock($"{containedType.GetCountExpression(schemaObject, fieldNumber)} == 1",
87+
return new IfElseBlock($"{ContainedType.GetCountExpression(schemaObject, fieldNumber)} == 1",
9088
then =>
9189
{
9290
then.Line(
93-
$"{updateFieldInstance} = new global::Improbable.Gdk.Core.Option<{Type}>({containedType.GetDeserializationExpression(schemaObject, fieldNumber)});");
91+
$"{updateFieldInstance} = new global::Improbable.Gdk.Core.Option<{Type}>({ContainedType.GetDeserializationExpression(schemaObject, fieldNumber)});");
9492
}).Format();
9593
}
9694

0 commit comments

Comments
 (0)