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
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); }