Published on 25th of October 2017
Rotary Selector

Over the next 4 lessons, we will consider how to write a demo application that will create a widget with a list of applications that can be launched, and if one of the applications, from this list, is not installed on the watch, run Samsung Galaxy Apps to download and install it.

We will look at Rotary Selector, which is widget not from the standard Elementary library, but from the EFL extension module. In addition, we will consider such modules of packages and applications manager, as modules for getting a list of installed applications and information about them; modules for running the installed application, and the ability to launch Samsung Galaxy Apps for downloading and installing the application.

As a basis, take the template created in the lesson 11. The project is available here (WearableUITemplate). In this lesson we'll look at the Rotary Selector widget: its main functions and data types, and will show how to add it to your demo application.

Introduction to the Rotary Selector

Rotary selector is a widget from the extension of the EFL library. It is not one of the standard Elementary library widgets; moreover it is specifically designed for round wearable devices. This widget consists of several elements, arranged in a circle on one or several pages of a wearable device.

Rotary Selector allows you to conveniently select items or go to the next or previous page by rotating the bezel. This widget will be useful if, for example, instead of using the standard list, you will add the application settings items or items for displaying and providing a selection of some categories in the application. In some cases, you can look at the rotary selector widget as a replacement for the standard list on the watch. Using this widget you can see more items on the screen and have a more similar look to the standard watch applications, if necessary.

Creating a Rotary Selector

Let's add the Rotary Selector widget to the demo application, and consider a few important functions. Firstly, expand the UIData structure by adding a pointer to the rotary selector widget:

typedef struct _UIData {
   Evas_Object *win;
   Evas_Object *conform;
   Evas_Object *rotary_selector;
} UIData;

Next, write a static function for creating and configuring the rotary selector widget:

static void
_rotary_selector_create(UIData *ui)
{
}

To create a rotary selector widget, you need to call the function inside the following function:

ui->rotary_selector = eext_rotary_selector_add(ui->conform);

Widget creation function takes a single parameter- a pointer to the parent component that will be responsible for removing of rotary selector widget.

As it was said above, the main advantage of the rotary selector widget is a quick selection of the elements and navigating through the pages using the bezel. To use this option, you must activate this feature for the widget by calling the following function:

void eext_rotary_object_event_activated_set(Evas_Object *obj, Eina_Bool activated);

The first parameter is a pointer to the rotary selector object, and the second one - a logical flag to activate or deactivate the navigation between the elements using the bezel. Call this function after creating a rotary selector to activate rotation:

static void
_rotary_selector_create(UIData *ui)
{
   ...

   eext_rotary_object_event_activated_set(ui->rotary_selector, EINA_TRUE);
}

Next, place the rotary selector object in the conformant and display it.

static void
_rotary_selector_create(UIData *ui)
{
   ...

   elm_object_content_set(ui->conform, ui->rotary_selector);
   evas_object_show(ui->rotary_selector);
}

After creating the conformant, call the function to create a widget.

static void
_conformant_create(UIData *ui)
{
   ...

   _rotary_selector_create(ui);

   ...
}

If you launch the application, on the screen will be nothing but the indicator of item selection, which located in the top right corner of the screen. If we start clicking on the widget, we can see how the indicator changes its state after the click; you can also try to rotate the bezel.

Adding Items to the Rotary Selector

Now let's add the elements to the round selector to see all the benefits from its managing. To add elements to the round selector, you need to call the following function:

Eext_Object_Item* eext_rotary_selector_item_append(Evas_Object *obj);

The function takes a single parameter - a pointer to the rotary selector object and returns the pointer to the structure for the created element. Let’s consider this function:

struct _Eext_Object_Item {
   Evas_Object *obj;
   void *data;
};

The first field is actually a pointer to the graphic data of the created element, but the second field is very interesting and useful, it is a pointer to the data, which can be used while working with the element. An example of using this field will be shown in the next lesson.

Let's add 15 elements to the application:

static void
_rotary_selector_create(UIData *ui)
{
   ...

   const int items_count = 15;
   for (int i = 0; i < items_count; ++i)
     {
        Eext_Object_Item *item = eext_rotary_selector_item_append(ui->rotary_selector);
     }

   ...
}

After receiving a pointer to the created element, you can configure it by adding text. To do so use following function:

void eext_rotary_selector_item_part_text_set(Eext_Object_Item *item,
                                             const char *part_name,
                                             const char *text);

The first parameter is the pointer to the element to which you want to set the text, the second parameter is the name of a text area, where the text will be set, and the third parameter is the text itself.

The element has two text areas:

"selector,main_text" – for the main text of the element

"selector,sub_text" – for the additional text of the element

Additional text of the element is optional, it is more like specifying information, and if the text has a long length, it will be displayed as ticker. In the application, let's add to the created items some information by calling the text adding function:

static void
_rotary_selector(UIData *ui)
{
   ...

   for (int i = 0; i < items_count; ++i)
     {
        ...

        const char MAX_TEXT_SIZE = 20;
        char item_text[MAX_TEXT_SIZE] = {'\0',};
        snprintf(item_text, MAX_TEXT_SIZE, " Element %d", i + 1);
        eext_rotary_selector_item_part_text_set(item, "selector,main_text", item_text);
        eext_rotary_selector_item_part_text_set(item,
                                                "selector,sub_text",
                                                "Extras. information ");
     }

   ...
}

You can start the application and see the result.

As expected, 15 items with basic and additional text were displayed on the screen. Moreover, you can see that since additional text has long length, it is displayed as a ticker. Also, when you rotate the bezel, you can see how selector moves around the circle of the screen, and if it reaches the end of the current page, it goes to the next one.

One of the main functions is the function of adding content to the element, note that the contents may vary depending on states of the element. To add content, use the following function:

void eext_rotary_selector_item_part_content_set(Eext_Object_Item *item,
                                                const char *part_name,
                                                Eext_Rotary_Selector_Item_State state,
                                                Evas_Object *content);

The first parameter is a pointer to the element of the rotary selector widget, the second parameter is the name of the area to which you will add content, the third parameter is the state of the element, in which the content will be displayed, the fourth parameter is a pointer to the object of the content that you put into the widget element. Here is the list of area names where the content can be inserted:

"item,icon" – for inserting a picture into an element;

"selector,content" – for changing picture for the item selection selector;

"item,bg_image" – for changing background of the element;

"selector,content" - for changing the content of the selector.

The most commonly used area is "item,bg_image".

Below are the states available for the element:

EEXT_ROTARY_SELECTOR_ITEM_STATE_NORMAL – element is in the normal state.

EEXT_ROTARY_SELECTOR_ITEM_STATE_PRESSED – element is pressed.

EEXT_ROTARY_SELECTOR_ITEM_STATE_DISABLED – element is not active.

Usage of this function will be demonstrated in the following lessons.

Along with the content installation, there is also a function to get the content of the element; it will be considered below.

Evas_Object* eext_rotary_selector_item_part_content_get(const Eext_Object_Item *item,
                                                        const char *part_name,
                                                        Eext_Rotary_Selector_Item_State state);

This function is similar to the above, but with one difference - instead of setting the content, the function returns it.

Below are the functions with descriptions, which you may need when working with the round selector.

const Eina_List* eext_rotary_selector_items_get(const Evas_Object *obj);

The function takes a pointer to the rotary selector widget, and returns a constant pointer (the read-only data) to the list of elements.

To get the currently selected item, use the following function:

Eext_Object_Item* eext_rotary_selector_selected_item_get(const Evas_Object *obj);

The function takes a pointer to the rotary selector widget, and returns a pointer to the currently selected item.

Function for setting the selection of the specified element:

void eext_rotary_selector_selected_item_set(Evas_Object *obj, Eext_Object_Item *item);

The first parameter is a pointer to the rotary selector; the second one is a pointer to an existing element that needs to be highlighted. When you call this function, the round selector will move the indicator to the specified item, so the widget supports selection, at a particular moment, of one item only.

There is one nuance in this function, which we will consider a little bit later.

Event Handling Rotary Selector

Let's examine the basic signals of the rotary selector widget, and add functions for processing these signals to the application.

The round selector supports two smart signals to which it is possible to add handlers:

"item,clicked" – signal when the item is clicked on the screen.

"item,selected" – signal when, by using the bezel, the element is selected.

Below is an example of how these signals can be used. Let’s add them to the demo application.

By adding calls that register handlers for the above listed signals, make changes to the function of rotary selector creation.

static void
_rotary_selector_create(UIData *ui)
{
   ...

   evas_object_smart_callback_add(ui->rotary_selector, "item,clicked", _item_clicked_cb, NULL);
   evas_object_smart_callback_add(ui->rotary_selector, "item,selected", _item_selected_cb, NULL);

   ...
}

Implement the signal handler functions, and make output to the log that the call of these handlers has been performed.

static void
_item_clicked_cb(void *data, Evas_Object *obj, void *event_info)
{
   dlog_print(DLOG_INFO, "lesson24", " Item is pressed");
}

static void
_item_selected_cb(void *data, Evas_Object *obj, void *event_info)
{
   dlog_print(DLOG_INFO, "lesson24", " Item is selected");
}

Do not forget to add header file of the logging module.

#include <dlog.h>

Run both the application and the logging window, and select the message type. In this case, select information messages, and then set the filter by a tag. If you rotate the bezel, in the logging window, when you change the active element, there will be displayed messages about an element selection. If you click on an item, it will display information about this action.

In the following lessons, we'll show you how to add functionality to these handlers. In this lesson, we give an example of using an element selection signal, but note that it will be used only in this lesson, and it will be removed in the following lessons.

In this lesson, we will give an example of using a signal of element selection, but note that it will be used only in this lesson, and in the following lessons it will be removed as well.

To change the color of the circular selector element, use the following function:

void eext_rotary_selector_item_part_color_set(Eext_Object_Item *item,
                                              const char *part_name,
                                              Eext_Rotary_Selector_Item_State state,
                                              int r,
                                              int g,
                                              int b,
                                              int a);

The first parameter is a pointer to an element, the second parameter is a name of the element area where you have to change the color, the third parameter is a state of the element (the list of states we considered earlier in this tutorial) from 4 to 7, the parameter and the color in RGBA format (values from 0 to 255 inclusively). Below is a list of the most frequently used areas:

"item,bg_image" – for the background of an element.

"item,icon" – for the item image.

"selector,icon" – for the image selection indicator.

For current example, we'll look at the "item, bg_image" area that indicates the background of the element. Add the header files, required to get the time indicators and to use the random number generator.

#include <time.h>
#include <stdlib.h>

Use current time as a seed for random number generator.

static bool
_app_create_cb(void *data)
{
   ...

   srand(time(NULL));

   ...
}

Make a change to the function of registering the handler for the item selection event by passing to the handler a pointer to the rotary selector, which will be used as user data inside the handler.

static void
_rotary_selector_create(UIData *ui)
{
   ...

   evas_object_smart_callback_add(ui->rotary_selector,
                                  "item,clicked",
                                  _item_clicked_cb,
                                  ui->rotary_selector);

   evas_object_smart_callback_add(ui->rotary_selector,
                                  "item,selected",
                                  _item_selected_cb,
                                  ui->rotary_selector);

   ...
}

In the handlers, call the function to get the current selected element of the round selector for its further repainting. Then by using the function of obtaining random numbers, generate numbers from 0 to 255 for RGB color, and set the received color to the background of the element in the element selection handler. In the click handler always paint an element in white.

static void
_item_clicked_cb(void *data, Evas_Object *obj, void *event_info)
{
   dlog_print(DLOG_INFO, "lesson24", " Item is pressed");

   if (!data) return;

   Evas_Object *rotary_selector = data;
   Eext_Object_Item *selected_item = eext_rotary_selector_selected_item_get(rotary_selector);

   eext_rotary_selector_item_part_color_set(selected_item,
                                            "item,bg_image",
                                            EEXT_ROTARY_SELECTOR_ITEM_STATE_NORMAL,
                                            255, 255, 255, 255);
}

static void
_item_selected_cb(void *data, Evas_Object *obj, void *event_info)
{
   dlog_print(DLOG_INFO, "lesson24", " Item is selected");

   if (!data) return;

   Evas_Object *rotary_selector = data;
   Eext_Object_Item *selected_item = eext_rotary_selector_selected_item_get(rotary_selector);

   int r = rand() % 256;
   int g = rand() % 256;
   int b = rand() % 256;

   eext_rotary_selector_item_part_color_set(selected_item,
                                            "item,bg_image",
                                            EEXT_ROTARY_SELECTOR_ITEM_STATE_NORMAL, r, g, b, 255);
}

Run the application.

Now if you start rotating the bezel, you'll see how the background color of the element changes randomly. The full code is available here WearLesson024

Traps and Pitfalls of Rotary Selector

As it was mentioned earlier we will consider the special behavior of the function for current element setting. Suppose you decided to install the last selected item each time you open the application (it was remembered after closing). After the creation of the element and its configuration, if it corresponds to the required condition (its data is equal to the data of the saved item), you should call the eext_rotary_selector_selected_item_set() function and set the created element to the selected state.

If you run the application, you will see that the selection indicator of the element is always on the first element, but not at the one you want. The problem is that at the moment of calling of this function the widget has not yet fully created, so when you call the element activation function, this element does not graphically exist yet. The solution of this problem is the delayed launch of the item selection function.

First, you should call a function to register the handler of the displaying rotary selector widget event:

evas_object_event_callback_add(rotary_selector, EVAS_CALLBACK_SHOW, _on_show_rotary, id_item);

Further in this handler it is necessary to create a timer with an interval, which will depend on the number of elements. For example, with 25 elements, 0.2 seconds is enough:

static void
_on_show_rotary(void *data, Evas *e, Evas_Object *obj, void *event_info)
{	
   show_selected_item_rotary = ecore_timer_add(0.2, _timer_select_item_rotary_cb, data);
}

In the timer handler, call the current element setting function:

static Eina_Bool
_timer_select_item_rotary_cb(void *data)
{
   ...

   eext_rotary_selector_selected_item_set(rotary_selector, selected_item);

   return ECORE_CALLBACK_DONE;
}

That's all, now when you launch the application, the selection indicator will be displayed on the item you have installed.

Usage of timers for launching pending procedures will be considered in more detail in the next lessons.

Leave a Reply

Your email address will not be published. Required fields are marked *