Mobile native

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:

  1. 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.

  2. 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;
    
  3. 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;
    
  4. 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;
    
  5. 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

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:

  1. 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
    
  2. 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
    
  3. 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 
    
  4. 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
    
  5. 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
    
  6. 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
    
  7. 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

Main view

To create the menu bar and views for the UI:

  1. 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
    
  2. 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
    
  3. Create the main menu after most of the containers are created:

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

    4. 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);
      
    5. 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);
      
    6. 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"
      
    7. 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);
      }
      
  4. 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);
    
  5. 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:

  1. 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.

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

  4. 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;
    }
    
Go to top