The IMF sample application demonstrates how to implement the default keyboard layout change using the Ecore_IMF API from the EFL framework. This sample covers only a subset of the available keyboard layouts. Additionally, the text autosave feature is demonstrated.
The following figure illustrates the application views.
Figure: IMF screens
The application interface consists of a toolbar with action buttons and an elm_entry component for text input.
You can click:
- Prev kbd to change the keyboard layout to the previous layout.
- Next kbd to change the keyboard layout to the next layout.
- Autosave to trigger the autosave feature. If enabled, the text provided in the elm_entry component is automatically saved to an external text file.
- Bind file to upload the related autosave text file content (if not empty) to the elm_entry component.
The keyboard with the appropriate layout is displayed as the elm_entry component gets the input focus. The following keyboard layouts are demonstrated:
- ELM_INPUT_PANEL_LAYOUT_NORMAL: common layout for text and number input with a switchable set of characters
- ELM_INPUT_PANEL_LAYOUT_NUMBERONLY: layout consisting of digits only
- ELM_INPUT_PANEL_LAYOUT_PHONENUMBER: common layout for phone number input and text messaging
- ELM_INPUT_PANEL_LAYOUT_MONTH: layout consisting of month numbers
Figure: UI component layout structure
Implementation
To implement the IMF application:
-
The controller_application_view_create() function in the controller module is responsible for calling the view creation-specific routines, callback attachment, and the "bind file" creation. If file does not exist, the _bind_file_create() function creates it and fills it with a default "Hello world !" string. Otherwise, it does nothing and the previous content is preserved. You can load the content to the entry component by clicking Bind file.
bool controller_application_view_create(void) { _bind_file_create(); if (!main_view_create()) { dlog_print(DLOG_ERROR, LOG_TAG,"View creation failed: on window handle"); main_view_destroy(); return false; } _attach_callbacks(); dlog_print(DLOG_DEBUG, LOG_TAG,"View creation successful"); return true; } static void _bind_file_create(void) { FILE *fp = NULL; char bind_file_full_path[FILE_PATH_MAX_LEN] = {0,}; data_path_compile(BIND_FILE, bind_file_full_path); // Check whether the file does not exist if (access(bind_file_full_path, F_OK)) { fp = fopen(bind_file_full_path, "w+"); dlog_print(DLOG_DEBUG, LOG_TAG, "File %s does not exist - creating", bind_file_full_path); if (!fp) { dlog_print(DLOG_DEBUG, LOG_TAG, "Could not create file"); return; } fwrite(bind_file_content, sizeof(char), sizeof(bind_file_content), fp); fclose(fp); } }
-
Another important module of in this sample is the top panel. It consists of 5 elementary UI components:
- 3 elm_button components to switch between the keyboard layouts and to bind the file to the entry component.
- elm_check component to trigger the autosave mode.
- elm_layout component, which contains all the above. The layout component uses the main_window.edc layout file to display the UI components in specified positions and with a defined appearance. Note that there are 2 groups in the mentioned file. The top panel uses the TOP_PANEL_GROUP group.
The top_panel_create() function creates 3 buttons and 1 checkbox and puts them in the layout container:
Evas_Object * top_panel_create(Evas_Object *parent) { char layout_file_full_path[FILE_PATH_MAX_LEN] = {0,}; if (s_view_data.top_panel_container) { return s_view_data.top_panel_container; } resource_path_compile(WIN_LAYOUT_CONTAINER_EDJ, layout_file_full_path); // Create the container s_view_data.top_panel_container = elm_layout_add(parent); if (!s_view_data.top_panel_container) { dlog_print(DLOG_ERROR, LOG_TAG,"Failure allocating resource: top panel container"); evas_object_del(s_view_data.top_panel_container); return s_view_data.top_panel_container = NULL; } if (!elm_layout_file_set(s_view_data.top_panel_container, layout_file_full_path, TOP_PANEL_GROUP)) { dlog_print(DLOG_ERROR, LOG_TAG,"Could not set layout file on top panel container"); evas_object_del(s_view_data.top_panel_container); return s_view_data.top_panel_container = NULL; } // Create buttons inside the container _button_create(&s_view_data.btn_layout_prev, s_view_data.top_panel_container, PREV_LAYOUT_BUTTON_PART); if (!s_view_data.btn_layout_prev) { dlog_print(DLOG_ERROR, LOG_TAG,"Failure allocating resource: previous layout button"); evas_object_del(s_view_data.top_panel_container); return s_view_data.top_panel_container = NULL; } _button_create(&s_view_data.btn_layout_next, s_view_data.top_panel_container, NEXT_LAYOUT_BUTTON_PART); if (!s_view_data.btn_layout_next) { dlog_print(DLOG_ERROR, LOG_TAG,"Failure allocating resource: next layout button"); evas_object_del(s_view_data.top_panel_container); return s_view_data.top_panel_container = NULL; } _checkbox_create(&s_view_data.check_autosave_toggle, s_view_data.top_panel_container, AUTOSAVE_ON_OFF_BUTTON_PART); if (!s_view_data.check_autosave_toggle) { dlog_print(DLOG_ERROR, LOG_TAG,"Failure allocating resource: save file button"); evas_object_del(s_view_data.top_panel_container); return s_view_data.top_panel_container = NULL; } _button_create(&s_view_data.btn_bind_file, s_view_data.top_panel_container, BIND_FILE_BUTTON_PART); if (!s_view_data.btn_bind_file) { dlog_print(DLOG_ERROR, LOG_TAG,"Failure allocating resource: load file button"); evas_object_del(s_view_data.top_panel_container); return s_view_data.top_panel_container = NULL; } return s_view_data.top_panel_container; }
-
The main view module contains the main_view_create() function. It is responsible for the main window, entry component, and top panel creation. Also, it attaches UI-related callbacks to the top panel (button clicks and checkbox check events).
bool main_view_create(void) { int screen_width = 0, screen_height = 0; s_view_data.win = elm_win_util_standard_add(PACKAGE, PACKAGE); if (!s_view_data.win) { dlog_print(DLOG_ERROR, LOG_TAG,"Failure allocating resource: main window"); return false; } elm_win_conformant_set(s_view_data.win, EINA_FALSE); elm_win_autodel_set(s_view_data.win, EINA_TRUE); elm_win_screen_size_get(s_view_data.win, NULL, NULL, &screen_width, &screen_height); evas_object_resize(s_view_data.win, screen_width, screen_height); evas_object_smart_callback_add(s_view_data.win, "delete,request", main_window_delete_cb, NULL); if (!_entry_container_create() || !_entry_create() || !(s_view_data.win_layout_top_panel = top_panel_create(s_view_data.win))) { return false; } // Position top panel, and show it evas_object_resize(s_view_data.win_layout_top_panel, screen_width, screen_height); evas_object_move(s_view_data.win_layout_top_panel, 0, 0); evas_object_show(s_view_data.win_layout_top_panel); _callbacks_attach(); // Show the window after the base GUI is set up evas_object_show(s_view_data.win); return true; }
-
The main view module also defines the IMF keypad layouts which are used by this application. Each time the user clicks Prev kbd or Next kbd, the _keypad_layout_change_cb() callback is called to set one of the defined keypad layouts:
static Elm_Input_Panel_Layout available_layouts[] = { ELM_INPUT_PANEL_LAYOUT_NORMAL, ELM_INPUT_PANEL_LAYOUT_NUMBERONLY, ELM_INPUT_PANEL_LAYOUT_PHONENUMBER, ELM_INPUT_PANEL_LAYOUT_MONTH }; static void _keypad_layout_change_cb(void *data, Evas_Object *obj, void *event_info) { data == LAYOUT_PREV ? --s_view_data.kbd_current_layout : ++s_view_data.kbd_current_layout; if (s_view_data.kbd_current_layout < 0) { s_view_data.kbd_current_layout = sizeof(available_layouts)/sizeof(Elm_Input_Panel_Layout) - 1; } if (s_view_data.kbd_current_layout >= sizeof(available_layouts)/sizeof(Elm_Input_Panel_Layout)) { s_view_data.kbd_current_layout = 0; } elm_entry_input_panel_hide(s_view_data.entry); elm_entry_input_panel_layout_set(s_view_data.entry, available_layouts[s_view_data.kbd_current_layout]); elm_entry_input_panel_show(s_view_data.entry); elm_object_focus_allow_set(s_view_data.entry, EINA_TRUE); elm_object_focus_set(s_view_data.entry, EINA_TRUE); }
-
The main view module implements public functions to be used by the controller to bind the file and activate and deactivate the autosave feature:
void main_view_bind_file_to_entry(const char *file_str) { if (!elm_entry_file_set(s_view_data.entry, file_str, ELM_TEXT_FORMAT_PLAIN_UTF8)) { dlog_print(DLOG_ERROR, LOG_TAG,"Failed to bind file %s . Bad file name ?", file_str); } elm_entry_autosave_set(s_view_data.entry, EINA_TRUE); }
The autosave feature works only when the file is bound to the entry component:
void main_view_entry_autosave_set(Eina_Bool on) { elm_entry_autosave_set(s_view_data.entry, on); dlog_print(DLOG_ERROR, LOG_TAG, "AUTOSAVE: %d", elm_entry_autosave_get(s_view_data.entry)); }