Wearable native

Creating Wearable Genlists

This tutorial shows how to create genlists, which are list components for large sets of elements. Genlists use callbacks to populate entries. The same UI component handles both flat lists and trees.

This feature is supported in wearable applications only.

Creating a Genlist

To initialize the application for a genlist and create the genlist component:

  1. Create a window entitled Genlist Basic Tutorial. It consists of a conformant component that contains a naviframe component. The genlist goes inside the naviframe.

    static bool
    _app_create(void *data)
    {
       appdata_s *app = data;
    
       app->win = elm_win_util_standard_add("main", "Genlist Basic Tutorial");
       elm_win_conformant_set(app->win, EINA_TRUE);
       evas_object_show(app->win);
       evas_object_resize(app->win, 480, 800);
       elm_win_autodel_set(app->win, EINA_TRUE);
    
       app->conformant = elm_conformant_add(app->win);
       evas_object_size_hint_weight_set(app->conformant, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
       elm_win_resize_object_add(app->win, app->conformant);
       evas_object_show(app->conformant);
    
       app->naviframe = elm_naviframe_add(app->win);
       evas_object_size_hint_weight_set(app->naviframe, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
       elm_win_resize_object_add(app->win, app->naviframe);
       evas_object_show(app->naviframe);
       elm_object_content_set(app->conformant, app->naviframe);
    
       _create_list(app);
       elm_naviframe_item_push(app->naviframe, NULL, NULL, NULL, app->list, NULL);
    
       return true;
    }
    
    int
    main(int argc, char **argv)
    {
       // Declare a few structures and zero-initialize (C99 feature)
       struct app_data app = {0};
       ui_app_lifecycle_callback_s event_callback = {0};
    
       event_callback.create = _app_create;
    
       // Run the mainloop
       return ui_app_main(&argc, &argv, &event_callback, &ad);
    }
    
  2. Declare the app_data struct:

    struct 
    app_data
    {
       Evas_Object *win;
       Evas_Object *naviframe;
       Evas_Object *conformant;
       Evas_Object *list;
       Elm_Genlist_Item_Class *itc;
       Elm_Genlist_Item_Class *itc2;
    };
    
  3. To create the genlist, call the elm_genlist_add() function:

    Evas_Object *list = elm_genlist_add(parent);
    

Figure: Genlist example

Genlist example

Adding List Entries

To add entries to the list:

  1. Build a basic item class:

    Elm_Genlist_Item_Class *itc = elm_genlist_item_class_new();
    itc->item_style = "default";
    itc->func.text_get = NULL;
    itc->func.content_get = NULL;
    itc->func.state_get = NULL;
    itc->func.del = NULL;
    

    This example uses the item class to set the item_style to default and all other fields to NULL. Note that this leaves out the text_get and content_get fields, which are used to add text and an icon to the list entry. In general, the item class fields define the APIs that are used to manage the list items.

  2. Add a new element to the list by calling the elm_genlist_item_append() function. Its second parameter is the item class structure which describes how to populate entries. Typically this structure is built once and re-used across calls to the elm_genlist_item_append() function.

    elm_genlist_item_append(list,
                            itc,
                            NULL, // Item data
                            NULL, // Parent item for trees, NULL if none
                            ELM_GENLIST_ITEM_NONE, // Item type; this is the common one
                            NULL, // Callback on selection of the item
                            // Data for that callback function
                            NULL);
    

    With most parameters as NULL and itc having most of its members NULL too, the elements of the list are blank and do not trigger anything when selected.

  3. Use the text_get() callback to add text in the elements in the Elm_Genlist_Item_Class structure.

    The callback must follow a prototype that takes data, an Evas Object, and a part variable as parameters. It returns a C string that is displayed in the part named after the part parameter. The callback is called for each user-settable text part according to the current theme.

    For more information on parts as used in the EFL files, see Writing a Simple EDC File.

    Note
    The value returned by the callback is freed by the EFL. The value must be freshly allocated: do not free it yourself and do not reuse it across list elements.

    The default theme (style defined for the item class) contains an elm.text part. The following example shows a possible implementation of the text_get() callback displaying text in the elm.text part:

    static char *
    _genlist_text_get(void *data, Evas_Object *obj, const char *part)
    {
       // Check this is text for the expected part
       if (strcmp(part, "elm.text") == 0) 
       {
          return strdup("Some text");
       }
       else 
       {
          return NULL;
       }
    }
    
    Note
    The part names and positions depend on the item_style chosen when adding new items to the genlist. Setting a custom theme makes it possible to completely change genlists by adding and moving parts. For more information, see Introduction to EDC Programming.

    The data parameter makes it possible to vary the list behavior according to the data given to the EFL in the data parameter of the elm_genlist_item_append() function call. For example, given an integer in that field through casting with (void *)(uintptr_t) i, you can get its value back using (int)(uintptr_t)data:

    static char *
    _genlist_text_get(void *data, Evas_Object *obj__UNUSED__, const char *part)
    {
       if (strcmp(part, "elm.text") == 0) 
       {
          char *buf = malloc(16);
          snprintf(buf, 16, "Entry %d.", (int)(uintptr_t)data);
    
          return buf;
       }
       else 
       {
          return NULL;
       }
    }
    
  4. Add icons to the list with the content_get() callback. It returns a pointer to an Evas_Object and is called for each part whose content can be set. The callback must follow a prototype that takes data, an Evas Object, and a part variable as parameters. The only difference with the text_get() callback is that the content_get() function returns an Evas_Object* rather than a char *.

    The following example creates colored rectangles in the parts that are to be set. In the default theme, this displays a red rectangle on the left side of each list item and a green one on the right side.

    static Evas_Object *
    _genlist_content_get(void *data, Evas_Object *obj, const char *part)
    {
       int i = (int) (uintptr_t) data;
    
       if (strcmp(part, "elm.swallow.icon") == 0) 
       {
          Evas_Object *bg = elm_bg_add(obj);
          elm_bg_color_set(bg, 255 * cos(i / (double) 10), 0, i % 255);
    
          return bg;
       }
       else if (strcmp(part, "elm.swallow.end") == 0) 
       {
          Evas_Object *bg = elm_bg_add(obj);
          elm_bg_color_set(bg, 0, 255 * sin(i / (double) 10), i % 255);
    
          return bg;
       }
       else 
       {
          return NULL;
       }
    }
    
  5. Note
    The swallow parts have no minimum size. This means that if you do not fix the minimum size, the part cannot be seen.
  6. Implement the genlist item events.

    Genlist items trigger a callback when clicked. The callback is chosen when adding the new item, for example, when calling the elm_genlist_item_append() function:

    elm_genlist_item_append(list, itc, NULL,  NULL,  ELM_GENLIST_ITEM_NONE,
                            _genlist_selected_cb, // Callback on selection of the item
                            NULL);
    

    The callback must follow a prototype that takes data, an Evas Object, and event information as parameters. The following implementation changes the item's style when the item is selected.

    static void
    _genlist_selected_cb(void *data, Evas_Object *obj, void *event_info)
    {
       appdata_s *app = data;
    
       Elm_Object_Item *it = (Elm_Object_Item*) event_info;
    
       elm_genlist_item_item_class_update(it, app->itc2);
    }
    

Managing Item Style and Size

You can manage the item style and size:

  • Managing the item style:

    The number of parts that you can fill for an item depends on the item style that you select when creating the item class (Elm_Genlist_Item_Class struct) for the genlist:

    app->itc->item_style = "default";
    

    For all available styles, see Genlist.

    You can customize the styles by modifying the themein the EDC file. In case the customization is only visual, it is a good practice to keep the same item style names for new themes. This makes it possible to change the theme and keep the code the same while also retaining the same overall item placement.

  • Setting a homogeneous item size:

    To set the scroller correctly, the actual height and width of the genlist must be computed at the beginning. Since this means computing the size of each item and adding all item sizes together, it has a cost and slows down the process of adding items to a genlist.

    The elm_genlist_homogeneous_set() function solves the problem by assuming that all items are the same size as the first item of the list. This assumption speeds up large insertions; however, it can lead to serious graphical issues if the items are not actually the same size. Use this function with care.

  • Changing the item class after the item has been created:

    Changing the item class of a UI component is an easy way to change its appearance based on user actions (for example, when it is selected). To change the item class, call the elm_genlist_item_item_class_update() function:

    static void
    _tree_item_expand_request(void *data, Evas_Object *o, void *event_info)
    {
       Elm_Object_Item *it = (Elm_Object_Item*) event_info;
    
       // Change the appearance and possibly content of the item being expanded
       elm_genlist_item_item_class_update(it, app->itc2);
    
       elm_genlist_item_expanded_set(it, EINA_TRUE);
    }
    

Using Item Modes

The previous genlist examples have all featured bare lists. However, the genlist component is able to display items in a tree or group mode:

  • Tree mode uses the parenting relationship with other items.

    The child elements are created on-demand when their parent is expanded, and deleted when it is contracted.

  • Group mode keeps an element visible as long as one of its children is visible.

    When scrolling, the parent element remains at the top of the UI component until all its child elements have been scrolled through and another group replaces it. The group mode is very useful for "title" items.

A common UI design is to mix the group and tree modes to allows for a tree behavior while keeping the group header item visible.

  • To use the group mode:

    1. Mark some elements as ELM_GENLIST_ITEM_GROUP and use the returned Elm_Object_Item to establish the parent-children relationship when adding the children items.

      Since there are 2 kind of items, create 2 item classes. Give them different styles and callback functions.

      app->itc = elm_genlist_item_class_new();
      app->itc->item_style = "default";
      app->itc->func.text_get = _genlist_text_get_size;
      app->itc->func.content_get = _genlist_content_get_bg;
      app->itc->func.state_get = NULL;
      app->itc->func.del = NULL; 
      
      app->itc2 = elm_genlist_item_class_new();
      app->itc2->item_style = "1text.1icon";
      app->itc2->func.text_get = _genlist_text_get_nosize;
      app->itc2->func.content_get = _genlist_content_get_icon;
      app->itc2->func.state_get = NULL;
      app->itc2->func.del = NULL; 
      
    2. Add a group header and follow it with 10 children. This is repeated 1000 times.

      The parent has the ELM_GENLIST_ITEM_GROUP type, while the children have the ELM_GENLIST_ITEM_NONE type.

      Note that the value returned by the elm_genlist_item_append() function for a group header is stored and sent to the elm_genlist_item_append() call that adds the children. This creates the parent-child relationship.

      for (i = 0; i < 1000; i++) 
      {
         it = elm_genlist_item_append(app->list, app->itc2, (void *)(uintptr_t) (10 * i), NULL,
                                      ELM_GENLIST_ITEM_GROUP, NULL, NULL);
         for (j = 0; j < 10; j++)
         {
            elm_genlist_item_append(app->list, app->itc, (void *)(uintptr_t) (10 * i + j), it,
                                    ELM_GENLIST_ITEM_NONE, NULL, NULL);
         }
      }
      
  • To use the tree mode:

    1. Register the callbacks (expand,request, expanded, contract,request, and contracted) using the evas_object_smart_callback_add() function on the genlist object:

      evas_object_smart_callback_add(app->list, "expand,request",
                                     _tree_item_expand_request, NULL);
      evas_object_smart_callback_add(app->list, "expanded",
                                     _tree_item_expanded, NULL);
      
      evas_object_smart_callback_add(app->list, "contract,request",
                                     _tree_item_contract_request, NULL);
      evas_object_smart_callback_add(app->list, "contracted",
                                     _tree_item_contracted, NULL);
      

      The expand,request and contract,request callbacks do only one thing: decide whether the element is expanded or contracted. This is done by using the elm_genlist_item_expanded_set() function; if it changes the expansion status of the item, the next callback is called (either expanded or contracted, depending on whether it was an expand,request or contract,request event). The following examples show a minimal implementation of these callbacks.

      static void
      _tree_item_expand_request(void *data, Evas_Object *o, void *event_info)
      {
         Elm_Object_Item *it = (Elm_Object_Item*) event_info;
      
         elm_genlist_item_item_class_update(it, app->itc2);
      
         elm_genlist_item_expanded_set(it, EINA_TRUE);
      }
      
      static void
      _tree_item_contract_request(void *data, Evas_Object *o, void *event_info)
      {
         Elm_Object_Item *it = (Elm_Object_Item*) event_info;
      
         elm_genlist_item_item_class_update(it, app->itc);
      
         elm_genlist_item_expanded_set(it, EINA_FALSE);
      }
      
      Note
      The above examples have an extra line: the call to the elm_genlist_item_item_class_update() function. This function changes the item style after the item creation.
    2. Expand the list. Once the genlist item status is set to expanded, the expanded event is triggered and it is the duty of a callback for that event to populate the list with the item's children. This relies on the parent parameter of functions (such as elm_genlist_item_append()), similar to the group mode.

      The following function is a callback implementation for the expanded event. It adds items that are built similarly to previous items, the only change is the parent parameter which is not NULL. Conveniently, the parent Elm_Object_Item pointer that is passed to the elm_genlist_item_append() function is given in the event_info callback and needs to be cast.

      static void
      _tree_item_expanded(void *data, Evas_Object *o, void *event_info)
      {
         Elm_Object_Item *it_parent = (Elm_Object_Item*) event_info;
         int i_parent = (int)(uintptr_t) data;
         int i;
      
         for (i = 0; i < 10; i++) 
         {
            elm_genlist_item_append(app->list, app->itc, (void *)(uintptr_t) (i + i_parent), it_parent,
                                    ELM_GENLIST_ITEM_NONE, NULL, NULL);
         }
      }
      
    3. Contract the list. The following code has the callback function for the contracted event. It simply calls the elm_genlist_item_subitems_clear() function to clear all children (including their own children if they have any) of the given item. Again, the item that is being contracted is available through the event_info parameter to the callback.

      static void
      _tree_item_contracted(void *data, Evas_Object *o, void *event_info)
      {
         Elm_Object_Item *it_parent = (Elm_Object_Item*) event_info;
      
         elm_genlist_item_subitems_clear(it_parent);
      }
      
  • To mix group and tree modes:

    1. Create an item of the group type, and an item of the tree type whose parent is the group type item.
    2. Add the callbacks to populate the children of the tree item in the regular way.
Go to top