Widgets Generation

You can generate widgets for your data type with Context menu / Create / New UI Widgets / Generate Widgets.

_images/widget-generation.png

Requirements

Data type should have at least one public field or public readable property of the supported types.

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
  • 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.

INotifyPropertyChanged Support

ObservableList<T> used by widgets provide support for INotifyPropertyChanged interface of the data type, so if property updated and was raised PropertyChanged event then widget will be updated.

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

Be careful not to overwrite modified scripts if you decide re-run widget generation.

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 generation 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 UnityEngine.UI.Text 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());
      }
   }
}