Layout - Content, Table and Box

This example shows how one can use the Layout widget to create a customized distribution of widgets on the screen, controlled by an Edje theme.

The full source code for this example can be found at layout_example_01.c.

Our custom layout is defined by a file, An example of layout theme file, which is an Edje theme file. Look for the Edje documentation to understand it. For now, it's enough to know that we describe some specific parts on this layout theme:

- a title text field;
- a box container;
- a table container;
- and a content container.

Going straight to the code, the following snippet instantiates the layout widget:

layout = elm_layout_add(win);
evas_object_size_hint_weight_set(layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
elm_win_resize_object_add(win, layout);
snprintf(buf, sizeof(buf), "%s/examples/layout_example.edj", elm_app_data_dir_get());
elm_layout_file_set(layout, buf, "example/mylayout");
evas_object_show(layout);
EVAS_HINT_EXPAND
#define EVAS_HINT_EXPAND
Use with evas_object_size_hint_weight_set(), evas_object_size_hint_weight_get(), evas_object_size_hin...
Definition: Evas_Common.h:297
elm_app_data_dir_get
const char * elm_app_data_dir_get(void)
Get the application's run time data prefix directory, as set by elm_app_info_set() and the way (envir...
Definition: elm_main.c:586
elm_layout_file_set
Eina_Bool elm_layout_file_set(Eo *obj, const char *file, const char *group)
Set the file that will be used as layout.
Definition: efl_ui_layout.c:3074
elm_layout_add
Evas_Object * elm_layout_add(Evas_Object *parent)
Add a new layout to the parent.
Definition: efl_ui_layout.c:3067
elm_win_resize_object_add
void elm_win_resize_object_add(Eo *obj, Evas_Object *subobj)
Add subobj as a resize object of window obj.
Definition: efl_ui_win.c:8992
evas_object_show
EVAS_API void evas_object_show(Evas_Object *eo_obj)
Makes the given Evas object visible.
Definition: evas_object_main.c:1814
evas_object_size_hint_weight_set
EVAS_API void evas_object_size_hint_weight_set(Evas_Object *obj, double x, double y)
Sets the hints for an object's weight.
Definition: evas_object_main.c:2638

As any other widget, we set some properties for the size calculation. But notice on this piece of code the call to the function elm_layout_file_set(). Here is where the theme file is loaded, and particularly the specific group from this theme file. Also notice that the theme file here is referenced as an .edj, which is a .edc theme file compiled to its binary form. Again, look for the Edje documentation for more information about theme files.

Next, we fetch from our theme a data string referenced by the key "title". This data was defined in the theme, and can be used as parameters which the program get from the specific theme that it is using. In this case, we store the title of this window and program in the theme, as a "data" entry, just for demonstration purposes:

// Setting title
const char *title = elm_layout_data_get(layout, "title");
if (title)
{
elm_win_title_set(win, title);
elm_object_part_text_set(layout, TITLE, title);
}
elm_object_part_text_set
void elm_object_part_text_set(Evas_Object *obj, const char *part, const char *label)
Set a text of an object.
Definition: elm_main.c:1485
elm_layout_data_get
const char * elm_layout_data_get(const Evas_Object *obj, const char *key)
Get the edje data from the given layout.
Definition: efl_ui_layout.c:3394
elm_win_title_set
void elm_win_title_set(Evas_Object *obj, const char *title)
Set the title of the window.
Definition: efl_ui_win.c:8636

This call elm_layout_data_get() is used to fetch the string based on the key, and elm_object_part_text_set() will set the part defined in the theme as "example/title" to contain this string. This key "example/title" has nothing special. It's just an arbitrary convention that we are using in this example. Every string in this example referencing a part of this theme will be of the form "example/<something>".

Now let's start using our layout to distribute things on the window space. Since the layout was added as a resize object to the elementary window, it will always occupy the entire space available for this window.

The theme already has a title, and it also defines a table element which is positioned approximately between 50% and 70% of the height of this window, and has 100% of the width. We create some widgets (two icons, a clock and a button) and pack them inside the table, in a distribution similar to a HTML table:

// Add icon, clock and button to the table
icon = elm_icon_add(win);
elm_icon_standard_set(icon, "home");
evas_object_size_hint_weight_set(icon, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(icon, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_layout_table_pack(layout, TABLE, icon, 0, 0, 1, 1);
evas_object_show(icon);
icon2 = elm_icon_add(win);
elm_icon_standard_set(icon2, "close");
evas_object_size_hint_weight_set(icon2, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(icon2, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_layout_table_pack(layout, TABLE, icon2, 1, 0, 1, 1);
evas_object_show(icon2);
clk = elm_clock_add(win);
evas_object_size_hint_weight_set(clk, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(clk, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_layout_table_pack(layout, TABLE, clk, 2, 0, 1, 1);
evas_object_show(clk);
bt = elm_button_add(win);
elm_object_text_set(bt, "Click me!");
evas_object_size_hint_weight_set(bt, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(bt, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_layout_table_pack(layout, TABLE, bt, 0, 1, 3, 1);
evas_object_smart_callback_add(bt, "clicked", _tbl_btn_cb, layout);
evas_object_show(bt);
EVAS_HINT_FILL
#define EVAS_HINT_FILL
Use with evas_object_size_hint_align_set(), evas_object_size_hint_align_get(), evas_object_size_hint_...
Definition: Evas_Common.h:298
elm_button_add
Evas_Object * elm_button_add(Evas_Object *parent)
Add a new button to the parent's canvas.
Definition: efl_ui_button.c:459
elm_clock_add
Evas_Object * elm_clock_add(Evas_Object *parent)
Add a new clock widget to the given parent Elementary (container) object.
Definition: elm_clock.c:797
elm_icon_add
Evas_Object * elm_icon_add(Evas_Object *parent)
Add a new icon object to the parent.
Definition: elm_icon.c:613
elm_icon_standard_set
Eina_Bool elm_icon_standard_set(Evas_Object *obj, const char *name)
Set the icon by icon standards names.
Definition: elm_icon.c:885
elm_layout_table_pack
Eina_Bool elm_layout_table_pack(Eo *obj, const char *part, Evas_Object *child, unsigned short col, unsigned short row, unsigned short colspan, unsigned short rowspan)
Insert child to layout table part.
Definition: efl_ui_layout.c:3139
evas_object_size_hint_align_set
EVAS_API void evas_object_size_hint_align_set(Evas_Object *obj, double x, double y)
Sets the hints for an object's alignment.
Definition: evas_object_main.c:2650
evas_object_smart_callback_add
EVAS_API void evas_object_smart_callback_add(Evas_Object *eo_obj, const char *event, Evas_Smart_Cb func, const void *data)
Add (register) a callback function to the smart event specified by event on the smart object obj.
Definition: evas_object_smart.c:1040

Notice that we just set size hints for every object, and call the function elm_layout_table_pack(), which does all the work. It will place the elements in the specified row/column, with row and column span if required, and then the object's size and position will be controlled by the layout widget. It will also respect size hints, alignments and weight properties set to these widgets. The resulting distribution on the screen depends on the table properties (described in the theme), the size hints set on each widget, and on the cells of the table that are being used.

For instance, we add the two icons and the clock on the first, second and third cells of the first row, and add the button the second row, making it span for 3 columns (thus having the size of the entire table width). This will result in a table that has 2 rows and 3 columns.

Now let's add some widgets to the box area of our layout. This box is around 20% and 50% of the vertical size of the layout, and 100% of its width. The theme defines that it will use an "horizontal flow" distribution to its elements. Unlike the table, a box will distribute elements without knowing about rows and columns, and the distribution function selected will take care of putting them in row, column, both, or any other available layout. This is also described in the Edje documentation.

This box area is similar to the Elm_Box widget of elementary, with the difference that its position and properties are controlled by the theme of the layout. It also contains more than one API to add items to it, since the items position now is defined in terms of a list of items, not a matrix. There's the first position (can have items added to it with elm_layout_box_prepend()), the last position (elm_layout_box_append()), the nth position (elm_layout_box_insert_at()) and the position right before an element (elm_layout_box_insert_before()). We use insert_at and prepend functions to add the first two buttons to this box, and insert_before on the callback of each button. The callback code will be shown later, but it basically adds a button just before the clicked button using the elm_layout_box_insert_before() function. Here's the code for adding the first 2 buttons:

item = elm_button_add(win);
elm_object_text_set(item, "Position 0");
evas_object_size_hint_weight_set(item, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(item, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_layout_box_insert_at(layout, BOX, item, 0);
evas_object_smart_callback_add(item, "clicked", _box_btn_cb, layout);
evas_object_show(item);
elm_layout_box_insert_at
Eina_Bool elm_layout_box_insert_at(Eo *obj, const char *part, Evas_Object *child, unsigned int pos)
Insert child to layout box part at a given position.
Definition: efl_ui_layout.c:3116
item = elm_button_add(win);
elm_object_text_set(item, "Prepended");
evas_object_size_hint_weight_set(item, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(item, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_layout_box_prepend(layout, BOX, item);
evas_object_smart_callback_add(item, "clicked", _box_btn_cb, layout);
evas_object_show(item);
elm_layout_box_prepend
Eina_Bool elm_layout_box_prepend(Eo *obj, const char *part, Evas_Object *child)
Prepend child to layout box part.
Definition: efl_ui_layout.c:3104

Finally, we have an area in this layout theme, in the bottom part of it, reserved for adding an specific widget. Differently from the 2 parts described until now, this one can only receive one widget with the call elm_object_part_content_set() for the layout. If there was already an item on this specific part, it will be deleted (one can use elm_object_part_content_unset() in order to remove it without deleting). An example of removing it without deleting, but manually deleting this widget just after that, can be seen on the callback for this button. Actually, the callback defined for this button will clean the two other parts (deleting all of their elements) and then remove and delete this button.

bt2 = elm_button_add(win);
elm_object_text_set(bt2, "Delete All");
elm_object_part_content_set(layout, SWALLOW, bt2);
evas_object_smart_callback_add(bt2, "clicked", _swallow_btn_cb, layout);
elm_object_part_content_set
void elm_object_part_content_set(Evas_Object *obj, const char *part, Evas_Object *content)
Set the content on part of a given container widget.
Definition: elm_main.c:1562

Also notice that, for this last added button, we don't have to call evas_object_show() on it. This is a particularity of the theme for layouts, that will have total control over the properties like size, position, visibility and clipping of a widget added with elm_object_part_content_set(). Again, read the Edje documentation to understand this better.

Now we just put the code for the different callbacks specified for each kind of button and make simple comments about them:

static void
_tbl_btn_cb(void *data, Evas_Object *btn, void *event_info EINA_UNUSED)
{
Evas_Object *layout = data;
elm_layout_table_unpack(layout, TABLE, btn);
evas_object_del(btn);
}
static void
_box_btn_cb(void *data, Evas_Object *btn, void *event_info EINA_UNUSED)
{
Evas_Object *layout = data;
Evas_Object *item;
char buf[30];
snprintf(buf, sizeof(buf), "Button %02d", _box_buttons++);
item = elm_button_add(elm_object_parent_widget_get(layout));
elm_object_text_set(item, buf);
evas_object_size_hint_weight_set(item, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(item, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_layout_box_insert_before(layout, BOX, item, btn);
evas_object_smart_callback_add(item, "clicked", _box_btn_cb, layout);
evas_object_show(item);
}
static void
_swallow_btn_cb(void *data, Evas_Object *btn EINA_UNUSED, void *event_info EINA_UNUSED)
{
Evas_Object *layout = data;
Evas_Object *item;
elm_layout_table_clear(layout, TABLE, EINA_TRUE);
elm_layout_box_remove_all(layout, BOX, EINA_TRUE);
item = elm_object_part_content_unset(layout, SWALLOW);
evas_object_del(item);
EINA_TRUE
#define EINA_TRUE
boolean value TRUE (numerical value 1)
Definition: eina_types.h:539
EINA_UNUSED
#define EINA_UNUSED
Used to indicate that a function parameter is purposely unused.
Definition: eina_types.h:339
elm_object_part_content_unset
Evas_Object * elm_object_part_content_unset(Evas_Object *obj, const char *part)
Unset the content on a part of a given container widget.
Definition: elm_main.c:1576
elm_layout_table_unpack
Evas_Object * elm_layout_table_unpack(Eo *obj, const char *part, Evas_Object *child)
Unpack (remove) a child of the given part table.
Definition: efl_ui_layout.c:3145
elm_layout_box_insert_before
Eina_Bool elm_layout_box_insert_before(Eo *obj, const char *part, Evas_Object *child, const Evas_Object *reference)
Insert child to layout box part before a reference object.
Definition: efl_ui_layout.c:3110
elm_layout_table_clear
Eina_Bool elm_layout_table_clear(Eo *obj, const char *part, Eina_Bool clear)
Remove all the child objects of the given part table.
Definition: efl_ui_layout.c:3153
elm_layout_box_remove_all
Eina_Bool elm_layout_box_remove_all(Eo *obj, const char *part, Eina_Bool clear)
Remove all children of the given part box.
Definition: efl_ui_layout.c:3130
elm_object_parent_widget_get
Evas_Object * elm_object_parent_widget_get(const Evas_Object *obj)
Get the first parent of the given object that is an Elementary widget.
Definition: elm_main.c:1833
evas_object_del
EVAS_API void evas_object_del(Evas_Object *obj)
Marks the given Evas object for deletion (when Evas will free its memory).
Definition: evas_object_main.c:928
Evas_Object
Efl_Canvas_Object Evas_Object
An Evas Object handle.
Definition: Evas_Common.h:185
}

The first callback is used for the button in the table, and will just remove itself from the table with elm_layout_table_unpack(), which remove items without deleting them, and then calling evas_object_del() on itself.

The second callback is for buttons added to the box. When clicked, these buttons will create a new button, and add them to the same box, in the position just before the clicked button.

And the last callback is for the button added to the "content" area. It will clear both the table and the box, passing `EINA_TRUE` to their respective `clear` parameters, which will imply on the items of these containers being deleted.

A screenshot of this example can be seen on: