Stopwatch Sample Overview

Mobile native

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

Stopwatch screen

Stopwatch structure Stopwatch component tree

Implementation

To create the stopwatch:

  1. 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;
    }
    
  2. 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

    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;
    } 
    
  3. 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);
       }
    }
    
  4. 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;
       }
    }