Creating Mobile Menus
This tutorial shows how to add menus to the application. The example code creates an application with 3 views, which can be accessed from a menu bar at the top of the application window. The application also has a hidden menu that appears only when the menu button is pressed.
This feature is supported in mobile applications only.
Defining the Menu Application Structure
To define the application structure:
-
Define the main structure of the application:
typedef struct appdata { Evas_Object* win; // Main window Evas_Object* layout; // Edje layout Evas_Object* conform; // Conform Evas_Object *nf; // Naviframe to handle the views appmenu_s *menu; // Main menu appmenu_s *sidemenu; // Side menu mainview_s *main_view; // Main view (dayselector) calview_s *cal_view; // Calendar view dateview_s *date_view; // Date and time view settview_s *settings_view; // Settings view Eina_Bool sdmenu_up; // Boolean variable to keep the side menu status } appdata_s;
This structure contains some specific variables for the views and the menus.
-
Define the main view by using the mainview structure. It is composed of a box (the main container), a dayselector, an img image, and an lb_main label.
typedef struct mainview { Evas_Object *box; // Main container of the view Evas_Object *colorselector; // Color selector Evas_Object *img; // Image Evas_Object *lb_day; // Label } mainview_s;
-
Define the date view. The date view is very similar to the main view. It contains a box, a datetime component and an lb_date label.
typedef struct dateview { Evas_Object *box; // Main container of the view Evas_Object *datetime; // Datetime component Evas_Object *lb_date; // Label } dateview_s;
-
Define the calendar view. It contains a box, a calendar and an lb_cal label.
typedef struct calview { Evas_Object *box; // Main container of the view Evas_Object *calendar; // Calendar component Evas_Object *lb_cal; // Label component } calview_s;
-
Define the toolbar and submenu items. The main menu is fixed and visible, and the side menu is hidden on application start. These menus are represented by the appmenu structure.
typedef struct appmenu { Evas_Object *tb; // Toolbar Elm_Object_Item *submenu; // Submenu item } appmenu_s;
Defining the Menus in the Application Theme
This tutorial uses the Basic EDC UI Application template, which defines the application theme in an EDC file.
The following figure shows the application theme structure.
Figure: Theme structure
The window, conformant, and layout are set by the Basic EDC UI Application template. You only need to set up the containers for the UI components and views.
To define the application theme for the menus and views:
-
To define the main menu at the top of the screen, create the menu/main SWALLOW part in the .edc file.
This part has 2 descriptions, one for the real menu position at the top named up, and another that is placed out of the screen as the default position.
part { name: "menu/main"; type: SWALLOW; description { state: "default" 0.0; rel1.relative: 0.0 1.0; rel2.relative: 1.0 1.0; } description { state: "up" 0.0; rel1.relative: 0.0 0.01; rel2.relative: 1.0 0.18; } } // End menu/main
-
Create the animation,menu_main program to change the main menu state at the application start. The state is changed from default to up, and linearly moves the menu from its out-of-the-screen position to the top of the screen.
program { name: "animation,menu_main"; source: ""; signal: "load"; action: STATE_SET "up" 1.0; target: "menu/main"; transition: LINEAR 0.5; } // END animation,menu_main
-
Create another SWALLOW part as a container for the views. This part also has 2 descriptions for animation purposes.
part { name: "view/main"; type: SWALLOW; description { state: "default" 0.0; rel1.relative: 0.0 1.0; rel2.relative: 1.0 1.0; } description { state: "up" 0.0; rel1.relative: 0.0 1.1; rel1.to: "menu/main"; rel2.relative: 1.0 1.0; color: 0 255 0 255; } } // END view/main
-
Create the animation,view_main program to move the view along the main menu:
program { name: "animation,view_main"; source: ""; signal: "load"; action: STATE_SET "up" 1.0; target: "view/main"; transition: LINEAR 0.2; } // END animation,view_main
-
Create the menu/side side menu container. In the default state, the side menu is hidden, and in the up state, it is shown on the left of the screen.
part { name: "menu/side"; type: SWALLOW; description { state: "default" 0.0; rel1.relative: -0.3 0.0; rel2.relative: -0.3 1.0; color: 255 0 0 255; } description { state: "up" 0.0; rel1.relative: 0.0 0.01; rel2.relative: 0.3 1.0; color: 255 0 0 255; } } // END menu/side
-
You can show the side menu by clicking the menu button.
Create the animation,menu_side program to run the related animation. This program runs when it receives a show,sidemenu event from the MenuButton source, and changes the side menu state to up, making the menu visible.
program { name: "animation,menu_side"; source: "MenuButton"; signal: "show,sidemenu"; action: STATE_SET "up" 1.0; target: "menu/side"; transition: LINEAR 0.2; } // END animation,menu_side
-
You can hide the side menu with another menu button click.
Create the animation,menu_side,hide program that starts when it receives a hide,sidemenu signal from the MenuButton source. It changes the side menu state back to default, hiding the menu.
program { name: "animation,menu_side,hide"; source: "MenuButton"; signal: "hide,sidemenu"; action: STATE_SET "default" 1.0; target: "menu/side"; transition: LINEAR 0.2; } // END animation,menu_side,hide
Creating the Menu Bar and Views
The naviframe handles views, which in this example are implemented as box containers. For more information, see the UI Containers guide.
The following figure shows the structure of the view/main container.
Figure: Main view
To create the menu bar and views for the UI:
-
Create the naviframe with the create_base_gui() function and allocate the memory to handle the views of the application:
// Memory allocation ad->main_view = calloc(1, sizeof(mainview_s)); // Allocate memory for the main view ad->cal_view = calloc(1, sizeof(calview_s)); // Allocate memory for the calendar view ad->date_view = calloc(1, sizeof(dateview_s)); // Allocate memory for the date view ad->settings_view = calloc(1, sizeof(settview_s)); // Allocate memory for the settings view // END of memory allocation
-
Use the app_terminate() function to free the memory for the views and menus:
app_terminate(void *data) { // Release all resources appdata_s *ad = data; free(ad->menu); free(ad->sidemenu); free(ad->main_view); free(ad->cal_view); free(ad->settings_view); } // END of app_terminate
-
Create the main menu after most of the containers are created:
-
Create a new _build_main_menu() function that takes appdata_s as a parameter. This function is called by the create_base_gui() function.
static void _build_main_menu(appdata_s *ad) { // Memory allocation for the main menu function appmenu_s *menu = calloc(1, sizeof(appmenu_s)); // Put the main menu in the application data ad->menu = menu;
-
Create a toolbar with the elm_toolbar_add() function. This toolbar is a child of the main window. Set ad->win as its parameter.
// Create the main menu toolbar menu->tb = elm_toolbar_add(ad->win);
-
Set up the behavior of the toolbar.
The display mode is set by using the elm_toolbar_shrink_mode_set() function. The toolbar does not scroll under the #ELM_TOOLBAR_SHRINK_NONE mode, but it enforces a minimum size, so that all the items fit inside it. It does not scroll or show the items that do not fit under the #ELM_TOOLBAR_SHRINK_HIDE mode. Finally, it scrolls under the #ELM_TOOLBAR_SHRINK_SCROLL mode, and it creates a button to aggregate items which did not fit with the #ELM_TOOLBAR_SHRINK_MENU mode.
// Set the "Menu" Toolbar properties elm_toolbar_shrink_mode_set(menu->tb, ELM_TOOLBAR_SHRINK_NONE);
In this example, there is only a limited number of menu elements and the ELM_TOOLBAR_SHRINK_NONE mode is used.
-
Expand the transverse length of the item according to the transverse length of the toolbar, giving EINA_TRUE as second parameter of the elm_toolbar_transverse_expanded_set() function.
elm_toolbar_transverse_expanded_set(menu->tb, EINA_TRUE);
-
Make the menu items have the same size by setting EINA_TRUE to the elm_toolbar_homogeneous_set() function. This activates the homogeneous mode of the toolbar.
elm_toolbar_homogeneous_set(menu->tb, EINA_FALSE);
-
Add menu items to the toolbar using the elm_toolbar_item_append() functions. It takes 5 parameters:
- Target toolbar
- Icon path for the menu item
- Item label (also used in the callback function)
- Function to call when the item is clicked
- Data to associate with the item for related callbacks.
// Add menu items to the main menu toolbar elm_toolbar_item_append(menu->tb, ICON_DIR"/home.png", "Home", _menu_item_selected_cb, ad); elm_toolbar_item_append(menu->tb, ICON_DIR"/calendar.png", "Calendar", _menu_item_selected_cb, ad); elm_toolbar_item_append(menu->tb, ICON_DIR"/clock.png", "Date", _menu_item_selected_cb, ad); elm_toolbar_item_append(menu->tb, ICON_DIR"/settings.png", "Settings", _menu_item_selected_cb, ad);
For the icons, add some images in the res/images application resource directory, and define a macro to contain this path in the application .h file. In this example, it is inc/menututorial.h.
Use the PACKAGE macro to setup this ICON_DIR macro. This way you can add items to the toolbar using icon images placed in the resource directory of the application.
#define ICON_DIR "/opt/usr/apps/"PACKAGE"/res/images"
- Show the toolbar and add the UI component to the main menu container:
// Show the UI component evas_object_show(menu->tb); // Add the UI component to the "menu/main" SWALLOW container elm_object_part_content_set(ad->layout, "menu/main", menu->tb); // Set the default view elm_toolbar_item_selected_set(elm_toolbar_first_item_get(menu->tb), EINA_TRUE); }
-
-
Enable switching between views based on the menu item selection.
When a menu item is selected, the _menu_item_selected_cb() callback is triggered. In this example, this same callback is used for all menu items. However, you can also create a separate callback for each item.
The callback must follow the Evas_Smart_Cb prototype that takes the application data, Evas Object, and event info as parameters.
In this example, the callback creates the requested view by recovering the calling object text to call the correct view creation function. The available views are Calendar, Date, Settings, and Home (main view). The view names are stored as the menu item labels, to be able to compare the returned string with the view names. When the name matches, the view is built by calling the correct function. Store the view in the application data and set up a new content to the naviframe before exiting.
The naviframe component destroys its content on each call of the elm_object_content_set() function. That is why the content must be built again on each item click.
it = ev; // Get the menu item text str = elm_object_item_text_get(it); // Compare with the possible view names if (!strcmp(str, "Calendar")) { // Build the "Calendar View" _build_calendar_view(ad); // Set the view from the application data view = ad->cal_view->box; } else if (!strcmp(str, "Date")) { // Build the "Date View" _build_date_view(ad); // Set the view from the application data view = ad->date_view->box; } else if (!strcmp(str, "Home")) { // Build the "Home or main View" _build_main_view(ad); // Set the view from the application data view = ad->main_view->box; } else if (!strcmp(str, "Settings")) { // Build the "Settings" view _build_settings_view(ad); // Set the view from the application data view = ad->settings_view->box; } else if (!strcmp(str, "Clock")) { // Build the "Date View" _build_date_view(ad); // Set the view from the application data view = ad->date_view->box; } // Show the view in the naviframe elm_object_content_set(ad->nf, view);
-
Define the functions which create the views. Each function creates a view and stores it in the application data.
static void _build_main_view(appdata_s *ad) { mainview_s *view = ad->main_view; char buf[PATH_MAX]; // Main box view->box = elm_box_add(ad->nf); elm_box_horizontal_set(view->box, EINA_FALSE); elm_box_homogeneous_set(view->box, EINA_TRUE); view->colorselector = elm_colorselector_add(view->box); elm_colorselector_mode_set(view->colorselector, ELM_COLORSELECTOR_PALETTE); elm_box_pack_start(view->box, view->colorselector); evas_object_show(view->colorselector); view->img = elm_image_add(view->box); evas_object_size_hint_weight_set(view->img, EVAS_HINT_FILL, EVAS_HINT_FILL); evas_object_size_hint_align_set(view->img, 0.5, 0.5); evas_object_size_hint_min_set(view->img, 256, 256); snprintf(buf, sizeof(buf), "%s/%s", ICON_DIR, "tizen-logo.png"); if (!elm_image_file_set(view->img, buf, NULL)) elm_object_text_set(view->lb_day, "Problem loading image"); elm_box_pack_start(view->box, view->img); evas_object_show(view->img); view->lb_day = elm_label_add(view->box); elm_object_text_set(view->lb_day, "Main view"); evas_object_size_hint_weight_set(view->lb_day, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); elm_box_pack_end(view->box, view->lb_day); evas_object_show(view->lb_day); } static void _build_calendar_view(appdata_s *ad) { calview_s *view = ad->cal_view; // Main box image = elm_image_add(win); view->box = elm_box_add(ad->nf); elm_box_horizontal_set(view->box, EINA_FALSE); elm_box_homogeneous_set(view->box, EINA_TRUE); view->calendar = elm_image_add(ad->nf); evas_object_size_hint_weight_set(view->calendar, EVAS_HINT_FILL, EVAS_HINT_FILL); evas_object_size_hint_align_set(view->calendar, 0.5, 0.5); evas_object_size_hint_min_set(view->calendar, 256, 256); elm_image_file_set(view->calendar, ICON_DIR"/calendar.png", NULL); elm_box_pack_start(view->box, view->calendar); evas_object_show(view->calendar); view->lb_cal = elm_label_add(view->box); elm_object_text_set(view->lb_cal, "The calendar view"); evas_object_size_hint_weight_set(view->lb_cal, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); elm_box_pack_end(view->box, view->lb_cal); evas_object_show(view->lb_cal); } // End of_build_calendar_view
Creating a Hidden Menu
To create a hidden menu:
-
Define the _build_side_menu() function to create the side menu toolbar and add some items to it. This function takes the application data as parameter and stores the built menu in the sidemenu attribute of the structure.
static void _build_side_menu(appdata_s *ad) { appmenu_s *sidemenu = calloc(1, sizeof(appmenu_s)); ad->sidemenu = sidemenu; sidemenu->tb = elm_toolbar_add(ad->win); elm_toolbar_shrink_mode_set(sidemenu->tb, ELM_TOOLBAR_SHRINK_EXPAND); elm_toolbar_transverse_expanded_set(sidemenu->tb, EINA_TRUE); elm_toolbar_item_append(sidemenu->tb, ICON_DIR"/home.png", "Home", _menu_item_selected_cb, ad); elm_toolbar_item_append(sidemenu->tb, ICON_DIR"/account.png", "Account", NULL, NULL); elm_toolbar_item_append(sidemenu->tb, ICON_DIR"/contacts.png", "Friends", NULL, NULL); elm_toolbar_item_append(sidemenu->tb, ICON_DIR"/clock.png", "Clock", _menu_item_selected_cb, ad); elm_toolbar_homogeneous_set(sidemenu->tb, EINA_FALSE); evas_object_show(sidemenu->tb); elm_object_part_content_set(ad->layout, "menu/side", sidemenu->tb); elm_toolbar_horizontal_set(sidemenu->tb, EINA_FALSE); elm_toolbar_item_selected_set(elm_toolbar_first_item_get(sidemenu->tb), EINA_TRUE); }
The side menu is created, but hidden by default. To make it appear, the menu (Home) button must be clicked.
-
Create the hidden menu functionality.
By default, the Basic EDC UI Application template creates a keydown_cb() function to handle the key down events. In the create_base_gui() function, an ecore_event_handler_add() function is called with the ECORE_EVENT_KEY_DOWN macro and with the keydown_cb() function as a callback. In this callback, the KEY_END event puts the window in the lower state.
keydown_cb(void *data , int type , void *event) { appdata_s *ad = data; Ecore_Event_Key *ev = event; if (!strcmp(ev->keyname, KEY_END)) { // Let window go to hidden state elm_win_lower(ad->win); } return ECORE_CALLBACK_DONE; }
-
Add the menu button key press handling to the keydown_cb() callback. The key name of the menu button is XF86Send. The menu is shown on the first press and hidden on the second press. Use Eina_Bool sdmenu_up on the application data to store the menu status during the application execution. If ad->sdmenu_up is EINA_TRUE, the menu is visible.
The animation,menu_side program is defined in the .edc theme file. This program is run when the show,sidemenu signal is received from the MenuButton source. In addition, the program that hides the side menu is defined and is called animation,menu_side,hide, starting on the hide,sidemenu signal.
Test the side menu status by sending the signals using the elm_object_signal_emit() function.
if (!strcmp(ev->keyname, "XF86Send")) { if (ad->sdmenu_up == EINA_TRUE) { // If the menu is visible send a "hide,sidemenu" signal elm_object_signal_emit(ad->layout, "hide,sidemenu", "MenuButton"); // Store the new menu status (hidden) ad->sdmenu_up = EINA_FALSE; } }
Now the menu appears and disappears when the menu button is pressed.
-
When the user clicks the first side menu button (Home), the menu disappears:
// Hide the side menu if it's visible if (ad->sdmenu_up == EINA_TRUE) { elm_object_signal_emit(ad->layout, "hide,sidemenu", "MenuButton"); ad->sdmenu_up = EINA_FALSE; }