Widgets Generator
You can generate widgets for your data type with Context menu / Create / New UI Widgets / Generate Widgets.
List of Generated Widgets
Autocomplete: requires at least one field or property of
string
type.AutoCombobox:
Combobox
withAutocomplete
to filter and select items by typing; requires at least one field or property ofstring
type.ComboboxMultiselect: same
Combobox
configured to display multiple selected values.DragInfo: displays content of dragged data
PickerListView:
Picker
to select the value fromListView
PickerTreeView:
Picker
to select the value fromTreeView
Requirements
[System.Serializable]
attribute.Supported types
Text types (string
or types convertible to the string
):
string
numeric data types (
int
,float
, etc)any type with overridden ToString() method and not derived from
UnityEngine.Object
.
Graphic types:
Sprite
Texture
,Texture2D
Color
Color32
Limitations
Autocomplete
Requires at least one field or property of the
string
type.Table
Requires at least one field or property of the text type.
Attributes
[GeneratorIgnore]
to mark fields or properties that should not be used in widgets[GeneratorAutocomplete]
to mark the field or property that should be used for autocomplete (will be used only first field with this attribute)
Known Problems
Widget generator does not work with struct
or interface
types inside a namespace with some Unity versions due to bug.
Workaround
interface
or struct
to class
in the type definition. Then run widgets generator and return type to interface
or struct
.Extending and Overriding Classes
All generated classes are marked as partial
to make possible it to split the definition of a class over two or more source files.
The recommended way to extending generated class is to create a new source file with class definition and add new methods or overridden methods to it.
It will prevent code loss in case of a new run of widgets generator for the same data type.
INotifyPropertyChanged and IObservable Support
ObservableList<T>
used by widgets provide support for INotifyPropertyChanged
and IObservable
interface of the data type, so widget will be updated if property changed and was raised corresponding event.INotifyPropertyChanged
or IObservable
implementation to your data type.IObservable
interface is preferable if you want to reduce memory allocations.public class ListViewIconsItemDescription : INotifyPropertyChanged
{
[SerializeField]
string name;
public string Name
{
get
{
return name;
}
set
{
if (name != value)
{
name = value;
Changed("Name");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void Changed(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
...
}
public class ListViewIconsItemDescription : IObservable
{
[SerializeField]
string name;
public string Name
{
get
{
return name;
}
set
{
if (name != value)
{
name = value;
Changed();
}
}
}
public event OnChange OnChange;
protected void Changed()
{
OnChange?.Invoke(this);
}
...
}
This way name of the first item displayed with the widget will be changed:
ListView.DataSource[0].Name = "New name";
You can disable this behavior with ObserveItems
property:
ListView.DataSource.ObserveItems = false;
// name displayed with the widget will be not changed
ListView.DataSource[0].Name = "New name";
Replacing generated code
Generated code can be freely modified.
Important
Collections
Widgets to display collections consist of the three classes:
your custom data type (class, struct or interface)
Widget class (required because of the generic components not allowed)
DefaultItem class to control tile view
Widget and DefaultItem classes created with widget generator for your type and you will need only to modify created DefaultItem class if it needs at all.
Functions to modify in the DefaultItem class:
SetData() to display passed data. Called when the item displayed or recycled.
MovedToCache() to unload unused resources like Sprite. Called when the item is out of sight and not be displayed or recycled (can happen when items list cleared).
For example you can replace default widgets used to display item fields with other widgets.
This example show Item.Number
field displayed with Spinner
instead of Text
and field value update with Spinner
changes.
Original code:
namespace UIWidgets.Examples.WidgetGeneration.Widgets
{
/// <summary>
/// ListView component for the DataItem.
/// </summary>
public class ListViewComponentDataItem : UIWidgets.ListViewItem,
UIWidgets.IResizableItem,
UIWidgets.IViewData<UIWidgets.Examples.WidgetGeneration.DataItem>
{
...
/// <summary>
/// The Number.
/// </summary>
public UIWidgets.TextAdapter Number;
...
/// <summary>
/// Gets the current item.
/// </summary>
public UIWidgets.Examples.WidgetGeneration.DataItem Item
{
get;
protected set;
}
/// <summary>
/// Sets component data with specified item.
/// </summary>
/// <param name="item">Item.</param>
public virtual void SetData(UIWidgets.Examples.WidgetGeneration.DataItem item)
{
Item = item;
if (Number != null)
{
Number.text = Item.Number.ToString();
}
...
}
...
}
}
New code:
namespace UIWidgets.Examples.WidgetGeneration.Widgets
{
/// <summary>
/// ListView component for the DataItem.
/// </summary>
public class ListViewComponentDataItem : UIWidgets.ListViewItem,
UIWidgets.IResizableItem,
UIWidgets.IViewData<UIWidgets.Examples.WidgetGeneration.DataItem>
{
...
/// <summary>
/// The Number.
/// </summary>
public UIWidgets.Spinner Number;
...
/// <summary>
/// Gets the current item.
/// </summary>
public UIWidgets.Examples.WidgetGeneration.DataItem Item
{
get;
protected set;
}
/// <summary>
/// Add callbacks.
/// </summary>
protected override void Start()
{
base.Start();
if (Number != null)
{
Number.onValueChangeInt.AddListener(UpdateNumber);
}
}
/// <summary>
/// Update Item.Number when spinner value changed.
/// </summary>
void UpdateNumber(int value)
{
Item.Number = value;
}
/// <summary>
/// Sets component data with specified item.
/// </summary>
/// <param name="item">Item.</param>
public virtual void SetData(UIWidgets.Examples.WidgetGeneration.DataItem item)
{
Item = item;
if (Number != null)
{
Number.Value = Item.Number;
}
...
}
/// <summary>
/// Remove callbacks.
/// </summary>
protected override void OnDestroy()
{
if (Number != null)
{
Number.onValueChangeInt.RemoveListener(UpdateNumber);
}
base.OnDestroy();
}
...
}
}
If you need to dynamicaly change the state of the objects like enabling or disabling them and restore state after item recycled then this can be done with SetData function:
public virtual void SetData(UIWidgets.Examples.WidgetGeneration.DataItem item)
{
Item = item;
// set state after item recycled
ToggableObject.setActive(item.IsToggableObjectActive);
...
}
Autocomplete
You can override Startswith, Contains, and GetStringValue functions to use different field or use other match condition.
This example show Text
field replaced with SomeOtherText
field and match with EndsWith
instead of Contains
.
Original code:
namespace UIWidgets.Examples.WidgetGeneration.Widgets
{
/// <summary>
/// Autocomplete for the DataItem.
/// </summary>
public class AutocompleteDataItem : UIWidgets.AutocompleteCustom<UIWidgets.Examples.WidgetGeneration.DataItem,
ListViewComponentDataItem, ListViewDataItem>
{
...
/// <summary>
/// Returns a value indicating whether Input occurs within specified value.
/// </summary>
/// <param name="value">Value.</param>
/// <returns>true if the Input occurs within value parameter; otherwise, false.</returns>
public override bool Contains(UIWidgets.Examples.WidgetGeneration.DataItem value)
{
if (CaseSensitive)
{
return value.Text.Contains(Query);
}
return value.Text.ToLower().Contains(Query.ToLower());
}
}
}
New code:
namespace UIWidgets.Examples.WidgetGeneration.Widgets
{
/// <summary>
/// Autocomplete for the DataItem.
/// </summary>
public class AutocompleteDataItem : UIWidgets.AutocompleteCustom<UIWidgets.Examples.WidgetGeneration.DataItem,
ListViewComponentDataItem, ListViewDataItem>
{
...
/// <summary>
/// Returns a value indicating whether Input occurs within specified value.
/// </summary>
/// <param name="value">Value.</param>
/// <returns>true if the Input occurs within value parameter; otherwise, false.</returns>
public override bool Contains(UIWidgets.Examples.WidgetGeneration.DataItem value)
{
if (CaseSensitive)
{
return value.SomeOtherText.EndsWith(Query);
}
return value.SomeOtherText.ToLower().EndsWith(Query.ToLower());
}
}
}