
The [UI Sample] MessageBubble View sample application demonstrates how to make a complex view by recursive composition of standard EFL UI components and containers in a UI component hierarchy.
It uses UI components, such as elm_conform and elm_scroller for the view management, containers, such as elm_box and elm_table for UI component management inside the view, and UI components, such as elm_button and elm_entry for the content inside view.
The following figure illustrates the message bubble view, its wireframe structure, and the UI component tree.
Figure: [UI Sample] MessageBubble screen
![[UI Sample] MessageBubble screen](/static/browse_samples/overview/native/Message_Bubble_UI/images/ui_messagebubble_sd.png)
![[UI Sample] MessageBubble screen](/static/browse_samples/overview/native/Message_Bubble_UI/images/ui_messagebubble_tree.png)
Application Layout
The create_base_gui() function is responsible for creating the application layout. It starts by creating a window, then adds elm_conformant to it to decorate the window with an indicator. elm_naviframe is added to act as a view manager of the window and provide the window title functionality. The main view is created using the create_main_view() function and added to the naviframe.
static void
create_base_gui(appdata_s *ad)
{
Evas_Object *main_scroller, *bg;
// Window
ad->win = elm_win_util_standard_add(PACKAGE, PACKAGE);
elm_win_conformant_set(ad->win, EINA_TRUE);
elm_win_autodel_set(ad->win, EINA_TRUE);
elm_win_indicator_mode_set(ad->win, ELM_WIN_INDICATOR_SHOW);
elm_app_base_scale_set(1.8);
if (elm_win_wm_rotation_supported_get(ad->win))
{
int rots[4] = {0, 90, 180, 270};
elm_win_wm_rotation_available_rotations_set(ad->win, (const int *)(&rots), 4);
}
evas_object_smart_callback_add(ad->win, "delete,request", win_delete_request_cb, NULL);
eext_object_event_callback_add(ad->win, EEXT_CALLBACK_BACK, win_back_cb, ad);
// Conformant
ad->conform = elm_conformant_add(ad->win);
elm_win_indicator_mode_set(ad->win, ELM_WIN_INDICATOR_SHOW);
elm_win_indicator_opacity_set(ad->win, ELM_WIN_INDICATOR_OPAQUE);
evas_object_size_hint_weight_set(ad->conform, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
elm_win_resize_object_add(ad->win, ad->conform);
evas_object_show(ad->conform);
// Indicator BG
bg = elm_bg_add(ad->conform);
elm_object_style_set(bg, "indicator/headerbg");
elm_object_part_content_set(ad->conform, "elm.swallow.indicator_bg", bg);
evas_object_show(bg);
// Naviframe
ad->nf = elm_naviframe_add(ad->conform);
evas_object_size_hint_weight_set(ad->nf, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
elm_object_content_set(ad->conform, ad->nf);
evas_object_show(ad->nf);
// Main view
main_scroller = create_main_view(ad);
elm_naviframe_item_push(ad->nf, "Message Bubble", NULL, NULL, main_scroller, NULL);
// Show the window after the base GUI is set up
evas_object_show(ad->win);
}
Main View
The create_main_view() function creates the main view content, the main scroller and main box. The main box contains 2 objects, a bubble scroller and an input field table. The scroller can be scrolled from the left-top to the left-bottom part of the screen.
static Evas_Object*
create_main_view(appdata_s *ad)
{
Evas_Object *main_scroller, *input_field_table;
main_scroller = elm_scroller_add(ad->nf);
elm_scroller_bounce_set(main_scroller, EINA_FALSE, EINA_TRUE);
elm_scroller_origin_reverse_set(main_scroller, EINA_FALSE, EINA_TRUE);
evas_object_size_hint_weight_set(main_scroller, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(main_scroller, EVAS_HINT_FILL, EVAS_HINT_FILL);
evas_object_show(main_scroller);
ad->main_box = elm_box_add(main_scroller);
elm_box_align_set(ad->main_box, 0, 0);
evas_object_size_hint_weight_set(ad->main_box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_show(ad->main_box);
A scroller is added to the main view, and a box structure to the scroller. The box is used for making a list of bubbles.
ad->bubble_scroller = elm_scroller_add(ad->main_box); elm_scroller_bounce_set(ad->bubble_scroller, EINA_FALSE, EINA_TRUE); evas_object_size_hint_weight_set(ad->bubble_scroller, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(ad->bubble_scroller, EVAS_HINT_FILL, EVAS_HINT_FILL); ad->bubble_box = elm_box_add(ad->bubble_scroller); elm_box_align_set(ad->bubble_box, 0, 0); evas_object_size_hint_weight_set(ad->bubble_box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_show(ad->bubble_box); elm_box_padding_set(ad->bubble_box, ELM_SCALE_SIZE(10), ELM_SCALE_SIZE(15)); elm_object_content_set(ad->bubble_scroller, ad->bubble_box); evas_object_show(ad->bubble_scroller); elm_box_pack_end(ad->main_box, ad->bubble_scroller);
An input field table is added to the main view using the create_input_field_table(ad) function. The input field table is a UI component containing buttons for entering and sending messages. This table is separate from the scroller.
input_field_table = create_input_field_table(ad); evas_object_show(input_field_table); elm_box_pack_end(ad->main_box, input_field_table); elm_object_content_set(main_scroller, ad->main_box);
After the main elements of the MessageBubble are created, the application is ready to load saved messages. The following sample example uses sample messages.
load_messages(ad); return main_scroller; }
Message bubbles are added using the create_bubble_table() function. This function creates a table container that includes 2 labels with a bubble background. To show the latest message at the bottom of the screen, use the elm_box_pack_start() function. To show the last message on top, use the elm_box_pack_end() function. You can see the latest messages, when the messages are loaded.
If there are several messages to be loaded, use the idler callback to load messages.
The create_input_field_table(ad) function creates the content of the view by composing a scroller structure that contains the screen elements. The scroller contains a box layout with the images.
static void
load_messages(appdata_s *ad)
{
Evas_Object *bubble_table;
ad->total_messages = NUM_OF_SAMPLE_MESSAGES;
ad->num_of_bubbles = 0;
int count = ad->total_messages - 1;
while (count >= 0)
{
bubble_table = create_bubble_table(ad->bubble_box, count % 2 + 1,
bubble_messages[count],
bubble_times[count]);
evas_object_show(bubble_table);
elm_box_pack_start(ad->bubble_box, bubble_table);
ad->num_of_bubbles++;
count--;
}
}
Create the layout with a table and button components using the create_bubble_table() function. The background is created as an Evas rectangle object, and its color is set with a rectangle object.
static Evas_Object *
create_bubble_table(Evas_Object *parent, Message_Bubble_Style style, const char *main_text, const char *sub_text)
{
Evas_Object *bubble_table, *button, *bg, *main_label, *sub_label;
Eina_Strbuf *strbuf = NULL;
char *buf = NULL;
bubble_table = elm_table_add(parent);
evas_object_size_hint_weight_set(bubble_table, EVAS_HINT_EXPAND, 0.0);
elm_table_padding_set(bubble_table, ELM_SCALE_SIZE(5), ELM_SCALE_SIZE(5));
evas_object_show(bubble_table);
button = elm_button_add(bubble_table);
elm_object_style_set(button, "transparent");
evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(button, EVAS_HINT_FILL, EVAS_HINT_FILL);
bg = evas_object_rectangle_add(evas_object_evas_get(button));
elm_object_content_set(button, bg);
evas_object_event_callback_add(button, EVAS_CALLBACK_MOUSE_DOWN, bubble_button_mouse_down_cb, bg);
evas_object_event_callback_add(button, EVAS_CALLBACK_MOUSE_UP, bubble_button_mouse_up_cb, bg);
evas_object_show(button);
Add 2 label components:
- Main label for the message
- Sub-label for showing a timestamp
Create 2 bubble components for the sender and receiver. The bubbles differ by their alignment, color, and position in the table.
main_label = elm_label_add(bubble_table);
elm_object_text_set(main_label, buf);
elm_label_wrap_width_set(main_label, ELM_SCALE_SIZE(BUBBLE_TEXT_WIDTH));
elm_label_line_wrap_set(main_label, ELM_WRAP_MIXED);
evas_object_size_hint_weight_set(main_label, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(main_label, EVAS_HINT_FILL, EVAS_HINT_FILL);
evas_object_repeat_events_set(main_label, EINA_TRUE);
evas_object_show(main_label);
sub_label = elm_label_add(bubble_table);
elm_object_text_set(sub_label, buf);
evas_object_size_hint_weight_set(sub_label, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_repeat_events_set(sub_label, EINA_TRUE);
evas_object_show(sub_label);
switch (style)
{
case MESSAGE_BUBBLE_SENT:
evas_object_size_hint_align_set(bubble_table, 1.0, 0.0);
evas_object_size_hint_align_set(sub_label, 1.0, EVAS_HINT_FILL);
evas_object_color_set(bg, 200, 170, 100, 255);
elm_table_pack(bubble_table, button, 0, 0, 1, 2);
elm_table_pack(bubble_table, main_label, 0, 0, 1, 1);
elm_table_pack(bubble_table, sub_label, 0, 1, 1, 1);
break;
case MESSAGE_BUBBLE_RECEIVE:
evas_object_size_hint_align_set(bubble_table, 0.0, 0.0);
evas_object_size_hint_align_set(sub_label, 0.0, EVAS_HINT_FILL);
evas_object_color_set(bg, 100, 170, 200, 255);
elm_table_pack(bubble_table, button, 0, 0, 1, 2);
elm_table_pack(bubble_table, main_label, 0, 0, 1, 1);
elm_table_pack(bubble_table, sub_label, 0, 1, 1, 1);
break;
case MESSAGE_BUBBLE_NONE:
case MESSAGE_BUBBLE_LAST:
default:
break;
}
return bubble_table;
}
Add the table component, button component, and the rectangle object in the layout using the create_input_field_table() function.
static Evas_Object *
create_input_field_table(appdata_s *ad)
{
Evas_Object *table, *button, *bg;
table = elm_table_add(ad->main_box);
elm_table_homogeneous_set(table, EINA_TRUE);
evas_object_size_hint_weight_set(table, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(table, EVAS_HINT_FILL, 1.0);
evas_object_show(table);
Add another button and rectangle to show the background color of the new entry. The entry is added on multiple lines with a guide text. Add callback functions to change the background color, when the entry focus state changes.
button = elm_button_add(table); elm_object_style_set(button, "transparent"); evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(button, EVAS_HINT_FILL, EVAS_HINT_FILL); bg = evas_object_rectangle_add(evas_object_evas_get(button)); elm_object_content_set(button, bg); evas_object_color_set(bg, 120, 220, 220, 255); evas_object_show(button); elm_table_pack(table, button, 0, 0, 3, 2); button = elm_button_add(table); elm_object_style_set(button, "transparent"); evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(button, EVAS_HINT_FILL, EVAS_HINT_FILL); bg = evas_object_rectangle_add(evas_object_evas_get(button)); elm_object_content_set(button, bg); evas_object_color_set(bg, 0, 0, 0, 0); evas_object_show(button); elm_table_pack(table, button, 0, 0, 2, 2); ad->input_field_entry = elm_entry_add(table); elm_object_part_text_set(ad->input_field_entry, "elm.guide", "Enter Message"); evas_object_size_hint_weight_set(ad->input_field_entry, EVAS_HINT_EXPAND, 0.0); evas_object_size_hint_align_set(ad->input_field_entry, EVAS_HINT_FILL, EVAS_HINT_FILL); evas_object_smart_callback_add(ad->input_field_entry, "focused", entry_focused_cb, bg); evas_object_smart_callback_add(ad->input_field_entry, "unfocused", entry_unfocused_cb, bg); evas_object_show(ad->input_field_entry); elm_table_pack(table, ad->input_field_entry, 0, 0, 2, 2);
Clicking the Send button appends a new bubble to the message bubble box. The button handler includes a callback for detecting button clicks.
button = elm_button_add(table); evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_align_set(button, 1.0, 1.0); elm_object_text_set(button, "SEND"); evas_object_smart_callback_add(button, "clicked", send_button_clicked_cb, ad); evas_object_show(button); elm_table_pack(table, button, 2, 1, 1, 1); return table; }
The send_message() function is called when the user clicks the Send button. It reads the text from the entry field and inserts it in a message bubble. The bubble is appended to the bottom of bubble box using the elm_box_pack_end() function.
Displaying the message bubble requires detecting the height of the bubble box. This is done using the elm_scroller_child_size_get() function. After this, the scroller is moved to the bottom of the screen using the elm_scroller_region_show() function.
static void
send_message(appdata_s *ad)
{
Evas_Coord w, h;
Evas_Object *bubble_table;
const char *main_text = NULL;
if (!ad->input_field_entry)
return;
main_text = elm_entry_entry_get(ad->input_field_entry);
if (!main_text || (strlen(main_text) == 0))
return;
bubble_table = create_bubble_table(ad->bubble_box, MESSAGE_BUBBLE_SENT,
elm_entry_entry_get(ad->input_field_entry),
"00:00 AM");
evas_object_show(bubble_table);
elm_box_pack_end(ad->bubble_box, bubble_table);
ad->num_of_bubbles++;
ad->total_messages++;
elm_entry_entry_set(ad->input_field_entry, "");
elm_scroller_child_size_get(ad->bubble_scroller, &w, &h);
elm_scroller_region_show(ad->bubble_scroller, 0, h, 0, 0);
}