~~Title: Naviframe Tutorial~~ ==== Naviframe Tutorial ==== Naviframes are containers useful for implementing interfaces with several screens having a previous/next relationship. {{ :naviframe.png?direct1100x439 |list}} **__//The whole code://__** {{/code_c/tutorial/naviframe/naviframe.c}} === Creating Naviframes === Naviframes are containers useful for implementing interfaces with several screens having a previous/next relationship. This tutorial shows a UI with three naviframes. Each naviframe is made of 20 screens, each made up of a label , a title with the same text, and previous and next buttons, which are used to navigate between the screens. The naviframe is "one-way": elements are added, and when the user clicks on the "previous" button, they are removed; there is no "next" button by default. To add it, we define a structure that holds the naviframe object along with a stack of the elements that the user has popped by using the "previous" button. Note that it is possible to create the elements on-the-fly each time the "next" button is pressed. Both approaches are valid. // NOTE: A zipper is a datastructure for an ordered set of elements and a // cursor in this set, meaning there are elements before the cursor (which are // stored inside the naviframe) and after (which are stored in the "popped" // list. struct naviframe_zipper { Evas_Object *naviframe; Eina_List *popped; }; To add several naviframes, create a function that factors their creation and initializes the ''naviframe_zipper'' structure defined above. static struct naviframe_zipper * _naviframe_add(Evas_Object *parent) { struct naviframe_zipper *z = malloc(sizeof(struct naviframe_zipper)); z->naviframe = elm_naviframe_add(parent); z->popped = NULL; evas_object_size_hint_weight_set(z->naviframe, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(z->naviframe, EVAS_HINT_FILL, EVAS_HINT_FILL); evas_object_show(z->naviframe); // By default, objects are destroyed when they are popped from the naviframe // To save and re-use them, enable "preserve_on_pop" elm_naviframe_content_preserve_on_pop_set(z->naviframe, EINA_TRUE); return z; } Create buttons that are at the top of the naviframe and allow the user to go back and forth between the screens. The naviframe widget builds a button for "previous" by default, but allows the programmers to provide their own buttons. It has a specific slot for the "next" button. // Save the element that is popped from the naviframe // callback of the prev button static void _naviframe_prev(void *data, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) { struct naviframe_zipper *z = data; //if more than one item push in naviframe if (elm_naviframe_bottom_item_get(z->naviframe) != elm_naviframe_top_item_get(z->naviframe)) { z->popped = eina_list_prepend(z->popped, elm_naviframe_item_pop(z->naviframe)); } } // Set the first element after the current one available and push it to the // naviframe //callback of the next button static void _naviframe_next(void *data, Evas_Object *o __UNUSED__, void *event_info __UNUSED__) { struct naviframe_zipper *z = data; Evas_Object *label, *prev, *next; const char *text; Elm_Object_Item *it; label = eina_list_data_get(z->popped); z->popped = eina_list_remove_list(z->popped, z->popped); if (label != NULL) { // The widget is saved inside the naviframe but nothing more; we need // to create new buttons and set the title text again (copy the one // from the label that is saved). text = elm_object_text_get(label); // The _button function creates a button which is either "Previous" (-1) or // "Next" (1) prev = _button(z, -1); next = _button(z, 1); it = elm_naviframe_item_push(z->naviframe, text, prev, next, label, NULL); } } When a naviframe and the pages that go inside it are built, populate it. Remember that three naviframes are created, each populated in a different way. The common bits have been factored out as a function and the specific parts are executed through a callback. The generic function is shown below. // Generic naviframe-populate function: // Its third (and last) parameter is a callback for customization, i.e. pushes // the new items to a specific position; it returns a "context" value that is // used between its calls and enables behaviors such as "push after the // previously-pushed item" static struct naviframe_zipper* _naviframe_populate_gen(Evas_Object *parent, const char *id, void * (*populate_cb) (Evas_Object *nav, const char *title, Evas_Object *prev, Evas_Object *next, Evas_Object *label, Elm_Object_Item *context) ) { struct naviframe_zipper *z; Evas_Object *label, *prev, *next; Elm_Object_Item *context = NULL; char buf[256]; int i; z = _naviframe_add(parent); for (i = 0; i < 20; i++) { label = elm_label_add(z->naviframe); snprintf(buf, sizeof(buf), "%s [%d]", id, i); elm_object_text_set(label, buf); evas_object_show(label); evas_object_size_hint_weight_set(label, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(label, EVAS_HINT_FILL, EVAS_HINT_FILL); // The _button function creates a button which is either "Previous" (-1) or // "Next" (1) prev = _button(z, -1); next = _button(z, 1); // Use the populate_cb callback to provide the customization of the way the // elements are added inside the naviframe context = populate_cb(z->naviframe, buf, prev, next, label, context); } return z; } The prototype of the callbacks is fairly large, but that is because of the syntax for callbacks in C. // Push items one after the other static Elm_Object_Item * _populate_cb__push(Evas_Object *nav, const char *title, Evas_Object *prev, Evas_Object *next, Evas_Object *label, Elm_Object_Item *context) { return elm_naviframe_item_push(nav, title, prev, next, label, NULL); } // Push items one after the other but use insert_after for it static Elm_Object_Item * _populate_cb__push_then_insert_after(Evas_Object *nav, const char *title, Evas_Object *prev, Evas_Object *next, Evas_Object *label, Elm_Object_Item *context) { if (context == NULL) { return elm_naviframe_item_push(nav, title, prev, next, label, NULL); } else { return elm_naviframe_item_insert_after(nav, context, title, prev, next, label, NULL); } } // Push one item and repeatedly insert new items before the last inserted // item static Elm_Object_Item * _populate_cb__push_then_insert_before(Evas_Object *nav, const char *title, Evas_Object *prev, Evas_Object *next, Evas_Object *label, Elm_Object_Item *context) { if (context == NULL) { return elm_naviframe_item_push(nav, title, prev, next, label, NULL); } else { return elm_naviframe_item_insert_before(nav, context, title, prev, next, label, NULL); } } Create a window with a vertical box, which holds the three naviframes from the ''elm_main()''. EAPI_MAIN int elm_main(int argc, char **argv) { Evas_Object *win; win = elm_win_util_standard_add("Naviframe", "Naviframe Tutorial"); elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED); elm_win_autodel_set(win, EINA_TRUE); Evas_Object *box; struct naviframe_zipper **z = malloc(3*sizeof(struct naviframe_zipper *)); box = elm_box_add(win); elm_box_horizontal_set(box, EINA_FALSE); elm_box_homogeneous_set(box, EINA_TRUE); 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); evas_object_show(box); elm_win_resize_object_add(win, box); z[0] = _naviframe_populate_gen(win, "Before", (void*)_populate_cb__push_then_insert_before); elm_box_pack_end(box, (*z)->naviframe); z[1] = _naviframe_populate_gen(win, "After", (void*)_populate_cb__push_then_insert_after); elm_box_pack_end(box, (*(z+1))->naviframe); z[2] = _naviframe_populate_gen(win, "Push", (void *)_populate_cb__push); elm_box_pack_end(box, (*(z+2))->naviframe); evas_object_smart_callback_add(win, "delete,request", _delete_cb, z); //win 400x400 px evas_object_resize(win, 400, 400); evas_object_show(win); elm_run(); return 0; } ELM_MAIN() \\ //**__The whole code__ : **//{{/code_c/tutorial/naviframe/naviframe.c}}