diff --git a/media/code_c/tutorial/naviframe/naviframe.c b/media/code_c/tutorial/naviframe/naviframe.c new file mode 100644 index 000000000..bd643aeab --- /dev/null +++ b/media/code_c/tutorial/naviframe/naviframe.c @@ -0,0 +1,227 @@ +#include + +#define __UNUSED__ __attribute__((__unused__)) + +// 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; +}; + +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; +} + +// forward declaration of the _button function since there is a circular +// reference below +static Evas_Object * _button(struct naviframe_zipper *, int direction); + +// 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); + } +} + +// Build either a "Previous" or a "Next" button +static Evas_Object * +_button(struct naviframe_zipper *z, int direction) +{ + Evas_Object *button; + Evas_Smart_Cb callback; + const char *text; + if (direction < 0) + { + text = "Previous"; + callback = _naviframe_prev; + } + else + { + text = "Next"; + callback = _naviframe_next; + } + button = elm_button_add(z->naviframe); + elm_object_text_set(button, text); + evas_object_smart_callback_add(button, "clicked", callback, z); + return button; +} + +// 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,*label2, *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); + label2 = 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; +} + +// 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); + } +} + +static void +_delete_cb(void *data, Evas_Object *obj, void *event_info) +{ + struct naviframe_zipper **z = data; + free(*z); + free(*(z+1)); + free(*(z+2)); + free(z); + evas_object_del(obj); +} + +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() diff --git a/media/naviframe.png b/media/naviframe.png new file mode 100644 index 000000000..9aee9f547 Binary files /dev/null and b/media/naviframe.png differ diff --git a/pages/docs.txt b/pages/docs.txt index 5301ff1c0..e630888de 100644 --- a/pages/docs.txt +++ b/pages/docs.txt @@ -41,6 +41,7 @@ Go check the current available version of EFL on each distro/platform: * [[tutorial/genlist_tutorial|Genlist Tutorial]] * [[tutorial/panes_tutorial|Panes Tutorial]] * [[tutorial/form_tutorial|Form Tutorial]] + * [[tutorial/naviframe_tutorial|Naviframe Tutorial]] ---- diff --git a/pages/tutorial/naviframe_tutorial.txt b/pages/tutorial/naviframe_tutorial.txt new file mode 100644 index 000000000..e3cbc68e1 --- /dev/null +++ b/pages/tutorial/naviframe_tutorial.txt @@ -0,0 +1,247 @@ +~~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}}