The Stopwatch sample demonstrates how you can implement a complex view using recursive composition of the standard EFL UI components and containers in a component hierarchy. It aims to explain how to use Ecore.
The sample uses UI components (such as elm_conformant, elm_layout, and elm_naviframe) for the view management, containers (such as elm_grid and elm_box) for component management inside the view, UI components (such as elm_bg, elm_button, elm_list, and elm_label) for the content inside the view, and Ecore (such as ecore_thread) for the operating main loop.
The following figure illustrates the main screen of the Stopwatch, the wireframe structure, and the component tree.
Figure: Stopwatch screen and structure
Implementation
To create the stopwatch:
- Initialize the user interface with the _create_stopwatch() callback function:
static void _create_stopwatch(appdata_s *ad) { Elm_Object_Item *nf_it = NULL; Evas_Object *box1 = NULL; Evas_Object *box2 = NULL; ret_if(!ad); _D("Create stopwatch"); // Window ad->win = _create_win(ad); ret_if(!ad->win); // Conformant ad->conform = _create_conform(ad); goto_if(!ad->conform, ERROR); // Indicator BG _set_indicator_bg(ad); // Naviframe ad->nf = _create_navi(ad); goto_if(!ad->nf, ERROR); // Layout ad->layout = _create_layout(ad); goto_if(!ad->layout, ERROR); // Three parts in the layout box1 = view_create_stopwatch_display(ad); goto_if(!box1, ERROR); elm_object_part_content_set(ad->layout, "part_one", box1); evas_object_data_set(ad->layout, PRIVATE_DATA_KEY_STOPWATCH_BOX1, box1); box2 = view_create_stopwatch_button(ad); goto_if(!box2, ERROR); elm_object_part_content_set(ad->layout, "part_two", box2); evas_object_data_set(ad->layout, PRIVATE_DATA_KEY_STOPWATCH_BOX2, box2); ad->list = view_create_stopwatch_list(ad); goto_if(!ad->list, ERROR); elm_object_part_content_set(ad->layout, "part_three", ad->list); evas_object_show(ad->layout); // Insert the layout to naviframe nf_it = elm_naviframe_item_push(ad->nf, "STOPWATCH", NULL, NULL, ad->layout, NULL); goto_if(!nf_it, ERROR); // Show the window after the base GUI is set up evas_object_show(ad->win); return; }
- The view_create_stopwatch_display(), view_create_stopwatch_button(), and view_create_stopwatch_list() functions create all the components and set them to the layout. To handle the button events, a smart clicked callback is registered with the evas_object_smart_callback_add() function.
Figure: Stopwatch main view layout
extern Evas_Object *view_create_stopwatch_display(appdata_s *ad) { Evas_Object *box = NULL; Evas_Object *grid = NULL; Evas_Object *label = NULL; Evas_Object *bg = NULL; viewdata_s *vd = NULL; retv_if(!ad, NULL); box = _create_box(ad->layout); retv_if(!box, NULL); grid = _create_grid(box); goto_if(!grid, ERROR); bg = _create_bg(grid, 1); goto_if(!bg, ERROR); elm_grid_pack(grid, bg, 0, 0, 100, 100); // Memory allocate vd = calloc(1, sizeof(viewdata_s)); goto_if(!vd, ERROR); ad->vd = vd; // Set the label vd->time = elm_label_add(grid); goto_if(!vd->time, ERROR); elm_object_text_set(vd->time, "<font_size=105><color=#ffffff>00:00:00</color></font_size>"); evas_object_show(vd->time); elm_grid_pack(grid, vd->time, 2, 10, 100, 100); vd->msec = elm_label_add(grid); goto_if(!vd->msec, ERROR); elm_object_text_set(vd->msec, "<font_size=50><color=#ffffff>.00</color></font_size>"); evas_object_show(vd->msec); elm_grid_pack(grid, vd->msec, 85, 38, 100, 100); // Stop the flag vd->stopped = EINA_FALSE; label = elm_label_add(grid); goto_if(!label, ERROR); elm_object_text_set(label, "<b><font_size=22><color=#a6a6a6>Hour " " Min Sec</color></font_size></b>"); evas_object_show(label); elm_grid_pack(grid, label, 10, 70, 100, 100); elm_box_pack_end(box, grid); return box; }
- The stopwatch_button_clicked() function checks the text on the button and calls the following functions:
- _stopwatch_start_cb() function starts changing the display using the ecore_thread_feedback_run() function.
- _stopwatch_stop_cb() function stops changing the display using the ecore_thread_cancel() and ecore_thread_check() functions.
- _stopwatch_lap_cb() function appends a lap time to an elm_list using the elm_list_item_append() function.
- _stopwatch_reset_cb() function resets the main view to the initial state.
extern void stopwatch_button_clicked(void *data, Evas_Object *obj, void *event_info) { const char *str = NULL; appdata_s *ad = NULL; ret_if(!data); ret_if(!obj); ad = data; str = elm_object_text_get(obj); tizen_error_e result = TIZEN_ERROR_UNKNOWN; if (str && !strcmp(str, "START")) { result = _stopwatch_start_cb(ad); retm_if(TIZEN_ERROR_NONE != result, "Failed to stopwatch start: %d", result); } else if (str && !strcmp(str, "STOP")) { result = stopwatch_stop_cb(ad); retm_if(TIZEN_ERROR_NONE != result, "Failed to stopwatch stop: %d", result); } else if (str && !strcmp(str, "LAP") && ad->start) { result = _stopwatch_lap_cb(ad); retm_if(TIZEN_ERROR_NONE != result, "Failed to stopwatch lap: %d", result); } else { result = _stopwatch_reset_cb(ad); retm_if(TIZEN_ERROR_NONE != result, "Failed to stopwatch reset: %d", result); } }
- The thread_job() function uses the ecore_thread_feedback() function to call the GUI functions from the main thread. The function that is used to handle the feedback simply sets the text of a label.
To cancel a running thread, the thread_job() function also uses the ecore_thread_check() function to check whether a thread is pending cancellation, because the ecore_thread_feedback_run() function can be used from the main loop.
extern void thread_job(void *data, Ecore_Thread *thread) { appdata_s *ad = NULL; viewdata_s *vd = NULL; int iteration = 0; ret_if(!data); ret_if(!thread); ad = data; vd = ad->vd; for (iteration = 0; ; iteration++) { vd->elapsedTime = ecore_time_get() - vd->startTime; ecore_thread_feedback(thread, (void*)(uintptr_t)iteration); usleep(10000); // You can have some real computation done if (ecore_thread_check(thread)) break; } }