Hi, I recently created a small project that utilized UI Toolkit in runtime and I wanted to share some thoughts.
I needed to create an app with UI that the user in the runtime could modify.
-
UI generation
To solve the UI generation part I thought I could deserialize UXML to VisualElement.
But as I found out there is no public API for that.
To In the end I wrote a simple XML ↔ VisualElement system. -
Handling Input Fields
App had to register special callbacks for each input field created by the user. This callback didn’t care for the type of the actual value being modified it just had to transform it into a string and save it.
I found myself with the following problems that I would like to give a spotlight to:
Non-generic BaseField<T>
BasseField<T>
has properties like labelElement
, label
, showMixedValue
, that have nothing to do with generic type and are impossible to access without it.
private void CreateUI(string[] ids, VisualElement root)
{
foreach (var id in ids)
{
BaseField field = CreateField(id);
field.label = "I wish I could do this";
root.Add(field);
}
}
// Type `BaseField` does not exists
private BaseField CreateField<T>(string id)
{
return id switch
{
"0" => new Toggle(),
"1" => new IntegerField(),
"2" => new Slider(),
};
}
Non-generic ChangeEvent
It would also make a life easier if I could register callback for any ChangeEvent<T>
.
Something like INotifyValueChanged<object>
.
private void CreateUI(string[] ids, VisualElement root)
{
foreach (var id in ids)
{
BaseField field = CreateField(id);
field.label = "I wish I could do this";
field.RegisterValueChangedCallback(evt => OnValueChange(evt.newValue));
root.Add(field);
}
}
private void OnValueChange(object value)
{
Debug.Log($"I really wish I could log like that: {value}");
}
In the end I had to code something like this:
private static void TryRegisterValueChangedCallback(VisualElement e,
EventCallback<ChangeEvent<object>> callback)
{
switch (e)
{
case INotifyValueChanged<bool> n:
n.RegisterValueChangedCallback(ev =>
callback?.Invoke(ChangeEvent<object>.GetPooled(ev.previousValue, ev.newValue)));
break; case INotifyValueChanged<int> n:
n.RegisterValueChangedCallback(ev =>
callback?.Invoke(ChangeEvent<object>.GetPooled(ev.previousValue, ev.newValue)));
break; case INotifyValueChanged<float> n:
n.RegisterValueChangedCallback(ev =>
callback?.Invoke(ChangeEvent<object>.GetPooled(ev.previousValue, ev.newValue)));
break; case INotifyValueChanged<string> n:
n.RegisterValueChangedCallback(ev =>
callback?.Invoke(ChangeEvent<object>.GetPooled(ev.previousValue, ev.newValue)));
break; default:
return;
}
}
UXML string to VisualElement in runtime
Is there a chance for UXML deserialization in runtime?