EFL: Using EFL Features for Designing the UI
This tutorial demonstrates how you can use EFL to create your application UI. EFL is an open-source toolkit that provides a set of libraries that offer useful features to applications. EFL covers a number of areas from data structures to UI component sets. EFL is one of the Tizen native UI modules and is available in various Tizen profiles. In Tizen, EFL is commonly used for UI applications, although you can also use it for non-UI applications.
Warm-up
Become familiar with the Efl Extension and Elementary API basics by learning about:
-
Learning EFL Basics
Learn the basic EFL characteristics and the EFL libraries supported in Tizen.
-
Using EFL in Tizen Native Applications
Start the Ecore main loop.
-
Using the Base Application Layout
Use the base layout with EFL for your application.
-
Creating a Simple Clock View
Create a simple clock view using a box, container UI component, and label to create a basic layout.
-
Creating a Complex Clock View
Create a complex clock view by organizing a basic layout using boxes.
Learning EFL Basics
EFL stands for Enlightenment Foundation Libraries. Enlightenment is a project consisting of the Enlightenment window manager, EFL libraries, and various utilities and applications. The Enlightenment team needed powerful libraries to handle, among others, rendering, main loops, and events, so EFL was introduced during the development of the Enlightenment window manager to fill these gaps. EFL is now widely used outside of the Enlightenment window manager.
Currently, five EFL libraries are publicly available to Tizen native applications. These libraries provide the following features, among others:
- Data structures
- Optimized rendering
- Thread pool
- Events and callbacks
- Fd handlers
- Timers and animators
- UI components
- Layout scripts
EFL Characteristics
EFL is aimed at not only desktop computers but also touch-screen and embedded devices, making EFL applications portable to many different types of devices. Applications do not need to care about the types of devices and profiles they run on. Instead of changing the code, it is typically enough to simply configure the applications for different devices.
The key characteristics of EFL include:
-
Performance
The main reason Tizen adopted EFL as its native toolkit is its speed. EFL is highly optimized by using a scene graph and retained-mode rendering. EFL is fast even in software rendering.
-
Small memory footprint
Despite its fast performance, EFL's memory footprint is smaller than that of other toolkits with similar features. A small memory footprint is useful in the embedded world, since embedded devices do not normally have much memory.
-
Back-end engine support
EFL supports several back-end engines, such as X11 (OpenGL, Xlib, Xcb), Wayland (OpenGL, SHM), Direct Framebuffer, DRM, memory buffers, PS3 native, Windows, and Mac OS. Applications do not need to deal with each back-end engine separately.
-
GUI and logic separation
EFL supports GUI layout and logic separation by having the layout description in a plain text file and the logic code in the C or C++ source files.
-
Themeable
An EFL theme can be changed at runtime without restarting the application. UI components are customizable so that each application can create its own customized theme to overlay above the default theme, adding customized versions of UI components to achieve a specific look and feel.
-
Scalable
EFL supports a scale factor that affects the size of objects in the application at runtime. By configuring the scale factor, applications can scale up and down as needed. The scale factor also supports a default setting that allows applications to scale nicely out-of-the-box.
-
Animations
EFL supports different types of animations. Evas supports Evas maps with low-level APIs for performing 2D, 2.5D, and 3D object transformations. Edje supports pre-defined transitions and wrapping of Evas maps. Elementary supports transit APIs for various types of animations, which can be combined.
-
Hardware acceleration
EFL supports OpenGL® and OpenGL-ES acceleration.
EFL Libraries
Figure: EFL libraries
Tizen supports the following EFL libraries:
-
Ecore
A clean and tiny event loop library with many convenience modules supporting operating system abstraction and integration. Ecore runs a main loop that is a key feature for event-driven applications and the rest of EFL. The Ecore main loop runs infinitely (or until requested to quit) and handles all registered events, such as touch, mouse, key, timers, animators, idlers, and fd handlers. Ecore also provides modules for handling network connections, files, input methods, and so on. EFL assumes that you run an Ecore main loop at all times (except at application startup and shutdown) and use the main loop to handle all callbacks (unless otherwise documented). EFL also assumes that threads use the Ecore infrastructure to return the results of their work back to the main loop for implementation.
-
Edje
A complex graphical design and layout library. Edje supports a separation between layout and logic. Edje was designed as a theme layer for moving the look, feel, and animation of objects into separate data files loaded at runtime, as opposed to being defined in-code. Using a plain text edc file, you can describe your application's layout without writing a line of code.
-
Eina
A library for data types and other useful tools. Eina provides data types such as lists, in-line lists, arrays, in-line arrays, hash tables, compact lists, iterator functions, sparse matrices, red-black trees, string buffers, shared string tokens, rectangle regions, generic value storage, and data models. Eina also supports tools such as benchmarking infrastructure, converters, counters, error handlers, basic file handlers, lazy allocators, loggers, magic number handlers, memory pools, modules, rectangles, safety checks, and string handlers.
-
Elementary
A UI component set library. Elementary is composed of several common UI components, such as buttons, check boxes, entries, lists, pop-ups, and windows. Some infrastructure is shipped with Elementary, such as scale configuration handling, finger-size configuration for touch UIs, theme handling, focus handling, accessibility, UI mirroring, and profile handling.
-
Evas
A highly optimized scene graph and rendering library. Evas is a stateful canvas scene graph in which you can place objects such as rectangles, lines, polygons, text, text blocks, images, and smart objects. Evas uses a scene graph to keep track of the state of all objects. You do not need to worry about objects repainting or keeping their states. You only need to modify them for the new state and let Evas take care of the rest. Evas supports anti-aliased text, smooth super and sub-sampled scaled images, alpha-blended objects, and more.
Using EFL in Tizen Native Applications
To start the Ecore main loop and move your application into the running state, call the ui_app_main() function. The Ecore main loop handles all general events, such as touch, mouse, key, and network events.
int main(int argc, char *argv[]) { ret = ui_app_main(argc, argv, &event_callback, &ad); return ret; }After your application is running:
- Create a window, and organize your UI components inside the window.
- Create the application logic code to be called when callbacks or timed events occurs (for example, animators for animations, timers for timeouts).
Using the Base Application Layout
This section provides how to use base layout with EFL for your application. Also, you can get some idea of sample layouts for applications.
Using the Base Layout
When developing a Tizen native application, it is recommended that you base your application layout on the base layout. The base layout supports the indicator and view management. The following figure shows the wireframe and UI component hierarchy of the base layout:
Figure: Base layout wireframe and UI component hierarchy
The UI components have the following roles:
- Window (Elm_win): Every UI component from Elementary is rendered in a Window.
- Conformant (Elm_Conformant): The Conformant supports the indicator area and resizing the application due to rotation or the ISF (keypad).
- Naviframe (Elm_Naviframe): The Naviframe acts as a view manager and optionally provides the application title. The main layout of the application is added to the Naviframe's view area.
Sample Applications with the Base Layout
Setting
The Setting application consists of a list to show as a menu. For organizing the application, create the application layout with a screen-size-list and place the layout into the Naviframe's view area.
Figure: Setting UI and layout
For more information, see the Setting sample application.
Application Store
For organizing the Application Store application, you need to add a scroller to the base layout, since the total height of the layout will be greater than the screen size, and you will therefore need to scroll the screen contents up and down. If the layout is larger than the screen, the scroller makes the view itself scrollable.
You can use a list or grid if the same objects are shown repeatedly. In this sample application, however, the various items are shown in a layout.
Figure: Application Store UI and layout
For more information, see the Application Store sample application.
Sample Applications with a Custom Layout
Calculator - No Naviframe
The Calculator is a good example of an exception to the base layout. The application has no view changes and no application title. As these are the two reasons why you should use a Naviframe, you do not need to add a Naviframe to the Calculator.
You can organize the application layout with container components. Container components are used for arranging UI components, both basic UI components and other container components.
Figure: Calculator UI and layout
For more information, see the Calculator sample application.
Email - Drawer
The Email application shows information using a list. The main view is the same as in the Setting application, consisting of a screen-size-list for displaying emails. However, the Email application has an additional feature: the Drawer, which is used for displaying the menu with a hierarchy.
To develop the application as a Tizen native application, add a layout on the Conformant, and add a Naviframe to the layout. This layout has a content area for the Drawer and Naviframe, and its style name is "layout/drawer/panel". To use this layout, use the following code:
layout = elm_layout_add(parent); elm_layout_theme_set(layout, "layout", "drawer", "panel");
The style includes parts for locating the Drawer and the main view. In the Drawer area, you can add a list to indicate the menu. In the main view, you can add a Naviframe to organize the view of the layout.
Figure: Email UI and layout
For more information, see the Email sample application.
Creating a Simple Clock View
This sample creates a basic clock view that shows the time, current city, and date. This sample demonstrates how to use a box, container component, and label to create a basic layout. The application includes the following UI components:
- Elm_window: Basic canvas for rendering the screen
- Elm_conformant: Support indicator area
- Elm_naviframe: View manager component
- Elm_box: Container component for layouting other UI components
- Elm_label: Basic UI component for showing text with a tag
The following figure illustrates the main view of the simple clock sample application and its wireframe structure.
Figure: Simple clock screen
The basic clock is implemented in the world_clock.c file. In this application, the data structure for application is following:
typedef struct appdata { // Save the window Evas_Object *win; } appdata_s;
The information in appdata is used for the entire system. In this sample application, the information is related to handling the HW back key.
The single callback function for application handling is app_create, and it only creates the basic GUI:
int main(int argc, char *argv[]) { appdata_s *ad = {0,}; app_event_callback_s event_callback = {0,}; int ret = 0; event_callback.create = app_create; event_callback.terminate = app_terminate; event_callback.pause = app_pause; event_callback.resume = app_resume; event_callback.app_control = app_control; ui_app_add_event_handler(&handlers[APP_EVENT_LOW_BATTERY], APP_EVENT_LOW_BATTERY, ui_app_low_battery, &ad); ui_app_add_event_handler(&handlers[APP_EVENT_LOW_MEMORY], APP_EVENT_LOW_MEMORY, ui_app_low_memory, &ad); ui_app_add_event_handler(&handlers[APP_EVENT_DEVICE_ORIENTATION_CHANGED], APP_EVENT_DEVICE_ORIENTATION_CHANGED, ui_app_orient_changed, &ad); ui_app_add_event_handler(&handlers[APP_EVENT_LANGUAGE_CHANGED], APP_EVENT_LANGUAGE_CHANGED, ui_app_lang_changed, &ad); ui_app_add_event_handler(&handlers[APP_EVENT_REGION_FORMAT_CHANGED], APP_EVENT_REGION_FORMAT_CHANGED, ui_app_region_changed, &ad); ui_app_remove_event_handler(handlers[APP_EVENT_LOW_MEMORY]); ret = ui_app_main(argc, argv, &event_callback, &ad); if (ret != APP_ERROR_NONE) { dlog_print(DLOG_ERROR, LOG_TAG, "ui_app_main() is failed. err = %d", ret); } return ret; } static bool app_create(void *data) { appdata_s *ad = data; create_base_gui(ad); return true; }
Use the create_base_gui() function to create a basic layout with a window, conformant and naviframe. This is the basic layout in the Tizen mobile environment.
ad->win = elm_win_util_standard_add(PACKAGE, PACKAGE); elm_win_autodel_set(ad->win, EINA_TRUE); // Conformant conform = elm_conformant_add(ad->win); elm_win_conformant_set(ad->win, EINA_TRUE); evas_object_size_hint_weight_set(conform, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); elm_win_resize_object_add(ad->win, conform); evas_object_show(conform); // Naviframe nf = elm_naviframe_add(conform); elm_object_content_set(conform, nf); evas_object_show(nf);
After creating a basic layout, create the main layout for the application. In this sample, it contains a box with 3 labels. The labels show the time, city and date. To add the labels to the box, use the elm_box_pack_end() function.
After adding the labels, you must set their text. You can set or decorate the text in the labels using the elm_object_text_set() function. You can modify the text size (font_size), color (color), and thickness (b).
// Add the box box = elm_box_add(nf); // Create a label label1 = elm_label_add(box); // Set text to the label with a tag elm_object_text_set(label1, "<font_size=110><color=#000000>07:26</color></font_size>"); // Add the label to the box elm_box_pack_end(box, label1); // Change label visibility evas_object_show(label1); // Repeat with other labels evas_object_show(box);
After creating the box with labels, set the box as a new view. You can also set the title of the application. These operations are handled by the naviframe using the elm_naviframe_item_push() function:
elm_naviframe_item_push(nf, _("World Clock"), NULL, NULL, box, "basic");
The parameters include the title, the name of the UI component added as a new view, and the naviframe style. The basic style is used for a simple view with normal title.
The following figure illustrates the UI component hierarchy of the application.
Figure: Simple clock components
Set the HW key handler. The Tizen mobile environment supports the More and back keys, but only the back key is handled in this sample.
eext_object_event_callback_add(ad->win, EEXT_CALLBACK_BACK, win_back_cb, ad);
After setting the callback function, the win_back_cb callback is invokes when a back key is pressed. The win_back_cb callback will hide the window:
static void win_back_cb(void *data , int type , void *event) { appdata_s *ad = data; elm_win_lower(ad->win); }
Creating a Complex Clock View
This sample creates a complex clock view. The sample demonstrates how to organize a basic layout using boxes. The application includes the following UI components:
- Elm_window: Basic canvas for rendering the screen
- Elm_conformant: Support indicator area
- Elm_naviframe: View manager component
- Elm_box: Container component for layouting other UI components
- Elm_label: Basic UI component for showing text with a tag
- Elm_genlist: List component
- Elm_button: Simple push button
The following figure illustrates the main view of the complex clock sample application and its wireframe structure.
Figure: Complex clock screen
Basic GUI
The basic clock is implemented in the world_clock.c file. In this application, the data structure for application is following:
typedef struct appdata { // Save the window Evas_Object *win; } appdata_s;
The information in appdata is used for the entire system. In this sample application, the information is related to handling the HW back key.
The single callback function for application handling is app_create, and it only creates the basic GUI:
int main(int argc, char *argv[]) { appdata_s *ad = {0,}; app_event_callback_s event_callback = {0,}; int ret = 0; event_callback.create = app_create; event_callback.terminate = app_terminate; event_callback.pause = app_pause; event_callback.resume = app_resume; event_callback.app_control = app_control; ui_app_add_event_handler(&handlers[APP_EVENT_LOW_BATTERY], APP_EVENT_LOW_BATTERY, ui_app_low_battery, &ad); ui_app_add_event_handler(&handlers[APP_EVENT_LOW_MEMORY], APP_EVENT_LOW_MEMORY, ui_app_low_memory, &ad); ui_app_add_event_handler(&handlers[APP_EVENT_DEVICE_ORIENTATION_CHANGED], APP_EVENT_DEVICE_ORIENTATION_CHANGED, ui_app_orient_changed, &ad); ui_app_add_event_handler(&handlers[APP_EVENT_LANGUAGE_CHANGED], APP_EVENT_LANGUAGE_CHANGED, ui_app_lang_changed, &ad); ui_app_add_event_handler(&handlers[APP_EVENT_REGION_FORMAT_CHANGED], APP_EVENT_REGION_FORMAT_CHANGED, ui_app_region_changed, &ad); ui_app_remove_event_handler(handlers[APP_EVENT_LOW_MEMORY]); ret = ui_app_main(argc, argv, &event_callback, &ad); if (ret != APP_ERROR_NONE) { dlog_print(DLOG_ERROR, LOG_TAG, "ui_app_main() is failed. err = %d", ret); } return ret; } static bool app_create(void *data) { appdata_s *ad = data; create_base_gui(ad); return true; }
The base GUI of the application contains the following elements:
- Clock
- List
- Main layout with buttons
Clock
The clock element contains 3 labels. The labels are packed as a single box component, which is part of the main layout.
static Evas_Object * create_clock(Evas_Object *nf) { Evas_Object *box, *label1, *label2, *label3; // Box box = elm_box_add(nf); label1 = elm_label_add(box); elm_object_text_set(label1, "<font_size=110><color=#000000>07:26</color></font_size>"); elm_box_pack_end(box, label1); evas_object_show(label1); evas_object_show(box); return box; }
List
This element contains a list of cities. The UI component used is genlist, which is a complex list able to show information with various styles.
static Evas_Object * create_list(Evas_Object *nf) { Evas_Object* list; Elm_Genlist_Item_Class *itc = NULL; int i, num_of_item; Elm_Object_Item *it; list = elm_genlist_add(nf);
To add items to the list, use the Elm_Genlist_Item_Class class. After adding a new class, set callback function to detect, when the item is rendered.
itc = elm_genlist_item_class_new(); itc->item_style = "2line.top.4"; itc->func.text_get = gl_text_get_cb; itc->func.content_get = NULL; itc->func.del = NULL;
In this application, all the list items are similar and use the same callback function (gl_text_get_cb()) for setting text, so only 1 item class is created:
static char* gl_text_get_cb(void *data, Evas_Object *obj, const char *part) { item_data_s *id = data; char buf[1024]; if (id->index == 0) { if (!strcmp(part, "elm.text.main.left.top")) { snprintf(buf, 1023, "%s", "07:26"); return strdup(buf); } else if (!strcmp(part, "elm.text.sub.right.top")) { snprintf(buf, 1023, "%s", "Cardiff"); return strdup(buf); } else if (!strcmp(part, "elm.text.sub.left.bottom")) { snprintf(buf, 1023, "%s", "Wen, Jan 1"); return strdup(buf); } else if (!strcmp(part, "elm.text.sub.right.bottom")) { snprintf(buf, 1023, "%s", "wales"); return strdup(buf); } } return NULL; }
Append items using the elm_genlist_item_append() function. In this application, 3 items are added:
num_of_item = 3; for (i = 0; i < num_of_item; i++) { item_data_s *id = calloc(sizeof(item_data_s), 1); id->index = i; it = elm_genlist_item_append(list, itc, id, NULL, ELM_GENLIST_ITEM_NONE, NULL, id); id->item = it; }
Main Layout with a Button
In the main layout, the other layout elements are merged together and a button added. The following example describes handling the main layout:
static void create_base_gui(appdata_s *ad) { Evas_Object *conform, *nf, *box, *clock, *layout, *rect, *button; // Window ad->win = elm_win_util_standard_add(PACKAGE, PACKAGE); elm_win_autodel_set(ad->win, EINA_TRUE); // Conformant conform = elm_conformant_add(ad->win); elm_win_conformant_set(ad->win, EINA_TRUE); evas_object_size_hint_weight_set(conform, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); elm_win_resize_object_add(ad->win, conform); evas_object_show(conform); // Naviframe nf = elm_naviframe_add(conform); elm_object_content_set(conform, nf); evas_object_show(nf); }
The box component is used to organize the main layout. The box is expanded as much as possible to fill the entire view.
// Box box = elm_box_add(nf); evas_object_size_hint_weight_set(box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(box, EVAS_HINT_FILL, EVAS_HINT_FILL);
The box contains the following elements:
-
The clock element contains 3 labels. The parameter for the evas_object_size_hint_weight_set() function is 0.1 meaning that the height of the clock box occupies 30% of the available area.
clock = create_clock(nf); evas_object_size_hint_weight_set(clock, EVAS_HINT_EXPAND, 0.3); evas_object_size_hint_align_set(clock, EVAS_HINT_FILL, EVAS_HINT_FILL); elm_box_pack_end(box, clock);
-
The list element is packed into the box. As the genlist elements does not have a determined size, its size depends on the layout parameter.
layout = elm_layout_add(box); elm_layout_theme_set(layout, "layout", "application", "default"); evas_object_size_hint_weight_set(layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(layout, EVAS_HINT_FILL, EVAS_HINT_FILL); rect = create_list(nf); elm_layout_content_set(layout, "elm.swallow.content", rect); evas_object_show(rect); evas_object_show(layout); elm_box_pack_end(box, layout);
-
The button element has a callback function to detect, when the button is clicked.
button = elm_button_add(box); evas_object_smart_callback_add(button, "clicked", btn_clicked_cb, NULL); evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, 0.1); evas_object_size_hint_align_set(button, EVAS_HINT_FILL, EVAS_HINT_FILL); elm_object_text_set(button, "Terminate"); evas_object_show(button); elm_box_pack_end(box, button);
The following figure illustrates the UI component hierarchy of the application.
Figure: Complex clock components
Add the main layout to the naviframe, and set the HW key handler:
eext_object_event_callback_add(ad->win, EEXT_CALLBACK_BACK, win_back_cb, ad);
After setting the callback function, the win_back_cb callback is invokes when a back key is pressed. The win_back_cb callback will hide the window:
static void win_back_cb(void *data , int type , void *event) { appdata_s *ad = data; elm_win_lower(ad->win); }