Tizen Native API
7.0
|
ecore_imf - How to handle preedit and commit string from Input Method Framework
This example demonstrates how to connect input method framework and handle preedit and commit string from input method framework.
To input Chinese, Japanese, Korean and other complex languages, the editor should be connected with input method framework.
How to initialize and shutdown ecore imf module
- ecore_imf_init() should be called to initialize and load immodule.
- ecore_imf_shutdown() is used for shutdowning and unloading immodule.
How to create input context and register pre-edit and commit event handler
Each entry should have each input context to connect with input service framework. Key event is processed by input method engine. The result is notified to application through ECORE_IMF_CALLBACK_PREEDIT_CHANGED and ECORE_IMF_CALLBACK_COMMIT event.
The full example follows.
#include <Ecore.h> #include <Ecore_Evas.h> #include <Ecore_IMF.h> #include <Ecore_IMF_Evas.h> #include <Evas.h> #include <stdio.h> #define WIDTH 480 #define HEIGHT 800 typedef struct _Entry Entry; struct _Entry { Evas_Object *rect; Evas_Object *txt_obj; Evas_Textblock_Style *txt_style; Evas_Textblock_Cursor *cursor; Evas_Textblock_Cursor *preedit_start; Evas_Textblock_Cursor *preedit_end; Ecore_IMF_Context *imf_context; Eina_Bool have_preedit : 1; }; static void _imf_cursor_info_set(Entry *en); static void _mouse_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info) { Entry *en = data; Evas_Event_Mouse_Down *ev = event_info; if (!en) return; if (en->imf_context) { Ecore_IMF_Event_Mouse_Down ecore_ev; ecore_imf_evas_event_mouse_down_wrap(ev, &ecore_ev); if (ecore_imf_context_filter_event(en->imf_context, ECORE_IMF_EVENT_MOUSE_DOWN, (Ecore_IMF_Event *)&ecore_ev)) return; // ecore_imf_context_reset should be called before calculating new cursor position ecore_imf_context_reset(en->imf_context); } // calculate new cursor position } static void _mouse_up_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info) { Entry *en = data; Evas_Event_Mouse_Up *ev = event_info; if (!en) return; if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) { _imf_cursor_info_set(en); return; } if (en->imf_context) { Ecore_IMF_Event_Mouse_Up ecore_ev; ecore_imf_evas_event_mouse_up_wrap(ev, &ecore_ev); if (ecore_imf_context_filter_event(en->imf_context, ECORE_IMF_EVENT_MOUSE_UP, (Ecore_IMF_Event *)&ecore_ev)) return; } if (en->rect) { if (evas_object_focus_get(en->rect)) { // notify cursor information _imf_cursor_info_set(en); } else evas_object_focus_set(en->rect, EINA_TRUE); } } static void _entry_focus_in_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info EINA_UNUSED) { Entry *en = data; if (!en) return; if (en->imf_context) ecore_imf_context_focus_in(en->imf_context); // notify the cursor information _imf_cursor_info_set(en); } static void _entry_focus_out_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info EINA_UNUSED) { Entry *en = data; if (!en) return; if (en->imf_context) { // ecore_imf_context_reset should be called for flushing the preedit string in focus-out event handler ecore_imf_context_reset(en->imf_context); ecore_imf_context_focus_out(en->imf_context); } } static void _canvas_focus_in_cb(void *data EINA_UNUSED, Evas *e, void *event_info EINA_UNUSED) { Entry *en; Evas_Object *obj = evas_focus_get(e); if (!obj) return; en = evas_object_data_get(obj, "Entry"); if (en) _entry_focus_in_cb(en, NULL, NULL, NULL); } static void _canvas_focus_out_cb(void *data EINA_UNUSED, Evas *e, void *event_info EINA_UNUSED) { Entry *en; Evas_Object *obj = evas_focus_get(e); if (!obj) return; en = evas_object_data_get(obj, "Entry"); if (en) _entry_focus_out_cb(en, NULL, NULL, NULL); } static void _imf_cursor_info_set(Entry *en) { Evas_Coord x, y, w, h; Evas_Coord cx, cy, cw, ch; // cursor geometry int cursor_pos; // cursor position in chars (Not bytes) Evas_BiDi_Direction dir; if (!en) return; // get cursor geometry if (en->txt_obj) evas_object_geometry_get(en->txt_obj, &x, &y, &w, &h); if (en->cursor && en->imf_context) { evas_textblock_cursor_geometry_get(en->cursor, &cx, &cy, &cw, &ch, &dir, EVAS_TEXTBLOCK_CURSOR_BEFORE); // get cursor position cursor_pos = evas_textblock_cursor_pos_get(en->cursor); ecore_imf_context_cursor_position_set(en->imf_context, cursor_pos); ecore_imf_context_cursor_location_set(en->imf_context, x + cx, y + cy, cw, ch); ecore_imf_context_bidi_direction_set(en->imf_context, (Ecore_IMF_BiDi_Direction)dir); } } static void _preedit_del(Entry *en) { if (!en || !en->have_preedit) return; if (!en->preedit_start || !en->preedit_end) return; if (!evas_textblock_cursor_compare(en->preedit_start, en->preedit_end)) return; // delete the preedit characters evas_textblock_cursor_range_delete(en->preedit_start, en->preedit_end); } static void _preedit_clear(Entry *en) { if (en->preedit_start) { evas_textblock_cursor_free(en->preedit_start); en->preedit_start = NULL; } if (en->preedit_end) { evas_textblock_cursor_free(en->preedit_end); en->preedit_end = NULL; } en->have_preedit = EINA_FALSE; } static Eina_Bool _ecore_imf_retrieve_surrounding_cb(void *data, Ecore_IMF_Context *ctx EINA_UNUSED, char **text, int *cursor_pos) { // This callback will be called when the Input Method Context module requests the surrounding context. Entry *en = data; const char *str; if (!en) return EINA_FALSE; str = evas_object_textblock_text_markup_get(en->txt_obj); if (text) *text = str ? strdup(str) : strdup(""); // get the current position of cursor if (cursor_pos && en->cursor) *cursor_pos = evas_textblock_cursor_pos_get(en->cursor); return EINA_TRUE; } static void _ecore_imf_event_delete_surrounding_cb(void *data, Ecore_IMF_Context *ctx EINA_UNUSED, void *event_info) { // called when the input method needs to delete all or part of the context surrounding the cursor Entry *en = data; Ecore_IMF_Event_Delete_Surrounding *ev = event_info; Evas_Textblock_Cursor *del_start, *del_end; int cursor_pos; if ((!en) || (!ev) || (!en->cursor)) return; // get the current cursor position cursor_pos = evas_textblock_cursor_pos_get(en->cursor); // start cursor position to be deleted del_start = evas_object_textblock_cursor_new(en->txt_obj); evas_textblock_cursor_pos_set(del_start, cursor_pos + ev->offset); // end cursor position to be deleted del_end = evas_object_textblock_cursor_new(en->txt_obj); evas_textblock_cursor_pos_set(del_end, cursor_pos + ev->offset + ev->n_chars); // implement function to delete character(s) from 'cursor_pos+ev->offset' cursor position to 'cursor_pos + ev->offset + ev->n_chars' evas_textblock_cursor_range_delete(del_start, del_end); evas_textblock_cursor_free(del_start); evas_textblock_cursor_free(del_end); } static void _ecore_imf_event_commit_cb(void *data, Ecore_IMF_Context *ctx EINA_UNUSED, void *event_info) { Entry *en = data; char *commit_str = (char *)event_info; if (!en) return; // delete preedit string _preedit_del(en); _preedit_clear(en); printf("commit string : %s\n", commit_str); // insert the commit string in the editor if (en->cursor && commit_str) evas_object_textblock_text_markup_prepend(en->cursor, commit_str); // notify the cursor information _imf_cursor_info_set(en); return; } static void _ecore_imf_event_preedit_changed_cb(void *data, Ecore_IMF_Context *ctx, void *event_info EINA_UNUSED) { // example how to get preedit string Entry *en = data; char *preedit_string; int cursor_pos; Eina_List *attrs = NULL; Eina_List *l; Ecore_IMF_Preedit_Attr *attr; Ecore_IMF_Context *imf_context = ctx; int preedit_start_pos, preedit_end_pos; int i; Eina_Bool preedit_end_state = EINA_FALSE; if (!en || !en->cursor) return; // get preedit string and attributes ecore_imf_context_preedit_string_with_attributes_get(imf_context, &preedit_string, &attrs, &cursor_pos); printf("preedit string : %s\n", preedit_string); if (!strcmp(preedit_string, "")) preedit_end_state = EINA_TRUE; // delete preedit _preedit_del(en); preedit_start_pos = evas_textblock_cursor_pos_get(en->cursor); // insert preedit character(s) if (strlen(preedit_string) > 0) { if (attrs) { EINA_LIST_FOREACH(attrs, l, attr) { if (attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB1) // style type { // apply appropriate style such as underline } else if (attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB2 || attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB3) { // apply appropriate style such as underline } } // insert code to display preedit string in your editor evas_object_textblock_text_markup_prepend(en->cursor, preedit_string); } } if (!preedit_end_state) { // set preedit start cursor if (!en->preedit_start) en->preedit_start = evas_object_textblock_cursor_new(en->txt_obj); evas_textblock_cursor_copy(en->cursor, en->preedit_start); // set preedit end cursor if (!en->preedit_end) en->preedit_end = evas_object_textblock_cursor_new(en->txt_obj); evas_textblock_cursor_copy(en->cursor, en->preedit_end); preedit_end_pos = evas_textblock_cursor_pos_get(en->cursor); for (i = 0; i < (preedit_end_pos - preedit_start_pos); i++) { evas_textblock_cursor_char_prev(en->preedit_start); } en->have_preedit = EINA_TRUE; // set cursor position evas_textblock_cursor_pos_set(en->cursor, preedit_start_pos + cursor_pos); } // notify the cursor information _imf_cursor_info_set(en); EINA_LIST_FREE(attrs, attr) free(attr); free(preedit_string); } static void _key_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info) { Entry *en = data; Evas_Event_Key_Down *ev = event_info; Eina_Bool control, alt, shift; if ((!en) || (!ev->key) || (!en->cursor)) return; if (en->imf_context) { Ecore_IMF_Event_Key_Down ecore_ev; ecore_imf_evas_event_key_down_wrap(ev, &ecore_ev); if (ecore_imf_context_filter_event(en->imf_context, ECORE_IMF_EVENT_KEY_DOWN, (Ecore_IMF_Event *)&ecore_ev)) return; } control = evas_key_modifier_is_set(ev->modifiers, "Control"); alt = evas_key_modifier_is_set(ev->modifiers, "Alt"); shift = evas_key_modifier_is_set(ev->modifiers, "Shift"); (void)alt; (void)shift; if (!strcmp(ev->key, "BackSpace")) { if (evas_textblock_cursor_char_prev(en->cursor)) { evas_textblock_cursor_char_delete(en->cursor); // notify the cursor information _imf_cursor_info_set(en); } return; } else if (!strcmp(ev->key, "Delete") || (!strcmp(ev->key, "KP_Delete") && !ev->string)) { // FILLME } else if ((control) && (!strcmp(ev->key, "v"))) { // ctrl + v // FILLME } else if ((control) && (!strcmp(ev->key, "a"))) { // ctrl + a // FILLME } else if ((control) && (!strcmp(ev->key, "A"))) { // ctrl + A // FILLME } else if ((control) && ((!strcmp(ev->key, "c") || (!strcmp(ev->key, "Insert"))))) { // ctrl + c // FILLME } else if ((control) && ((!strcmp(ev->key, "x") || (!strcmp(ev->key, "m"))))) { // ctrl + x // FILLME } else if ((control) && (!strcmp(ev->key, "z"))) { // ctrl + z (undo) // FILLME } else if ((control) && (!strcmp(ev->key, "y"))) { // ctrl + y (redo) // FILLME } else if ((!strcmp(ev->key, "Return")) || (!strcmp(ev->key, "KP_Enter"))) { // FILLME } else { if (ev->string) { printf("key down string : %s\n", ev->string); evas_object_textblock_text_markup_prepend(en->cursor, ev->string); } } // notify the cursor information _imf_cursor_info_set(en); } static void _key_up_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info) { Entry *en = data; Evas_Event_Key_Up *ev = event_info; if (!en) return; if (en->imf_context) { Ecore_IMF_Event_Key_Up ecore_ev; ecore_imf_evas_event_key_up_wrap(ev, &ecore_ev); if (ecore_imf_context_filter_event(en->imf_context, ECORE_IMF_EVENT_KEY_UP, (Ecore_IMF_Event *)&ecore_ev)) return; } } static void create_input_field(Evas *evas, Entry *en, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h) { if (!en) return; en->have_preedit = EINA_FALSE; en->preedit_start = NULL; en->preedit_end = NULL; // create the background for text input field en->rect = evas_object_rectangle_add(evas); evas_object_color_set(en->rect, 150, 150, 150, 255); // gray color evas_object_move(en->rect, x, y); evas_object_resize(en->rect, w, h); evas_object_show(en->rect); evas_object_data_set(en->rect, "Entry", en); // create text object for displaying text en->txt_obj = evas_object_textblock_add(evas); evas_object_color_set(en->txt_obj, 0, 0, 0, 255); evas_object_pass_events_set(en->txt_obj, EINA_TRUE); evas_object_move(en->txt_obj, x, y); evas_object_resize(en->txt_obj, w, h); evas_object_show(en->txt_obj); // set style on textblock static const char *style_buf = "DEFAULT='font=Sans font_size=30 color=#000 text_class=entry'" "newline='br'" "b='+ font=Sans:style=bold'"; en->txt_style = evas_textblock_style_new(); evas_textblock_style_set(en->txt_style, style_buf); evas_object_textblock_style_set(en->txt_obj, en->txt_style); // create cursor en->cursor = evas_object_textblock_cursor_new(en->txt_obj); // create input context const char *default_id = ecore_imf_context_default_id_get(); if (!default_id) { fprintf(stderr, "Can't create ecore_imf_context\n"); return; } en->imf_context = ecore_imf_context_add(default_id); ecore_imf_context_client_canvas_set(en->imf_context, evas); // register key event handler evas_object_event_callback_add(en->rect, EVAS_CALLBACK_KEY_DOWN, _key_down_cb, en); evas_object_event_callback_add(en->rect, EVAS_CALLBACK_KEY_UP, _key_up_cb, en); // register mouse event handler evas_object_event_callback_add(en->rect, EVAS_CALLBACK_MOUSE_DOWN, _mouse_down_cb, en); evas_object_event_callback_add(en->rect, EVAS_CALLBACK_MOUSE_UP, _mouse_up_cb, en); // register focus event handler evas_object_event_callback_add(en->rect, EVAS_CALLBACK_FOCUS_IN, _entry_focus_in_cb, en); evas_object_event_callback_add(en->rect, EVAS_CALLBACK_FOCUS_OUT, _entry_focus_out_cb, en); // register retrieve surrounding callback ecore_imf_context_retrieve_surrounding_callback_set(en->imf_context, _ecore_imf_retrieve_surrounding_cb, en); // register commit event callback ecore_imf_context_event_callback_add(en->imf_context, ECORE_IMF_CALLBACK_COMMIT, _ecore_imf_event_commit_cb, en); // register preedit changed event handler ecore_imf_context_event_callback_add(en->imf_context, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, _ecore_imf_event_preedit_changed_cb, en); // register surrounding delete event callback ecore_imf_context_event_callback_add(en->imf_context, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, _ecore_imf_event_delete_surrounding_cb, en); } static void delete_input_field(Entry *en) { if (!en) return; if (en->rect) { evas_object_del(en->rect); en->rect = NULL; } if (en->cursor) { evas_textblock_cursor_free(en->cursor); en->cursor = NULL; } if (en->preedit_start) { evas_textblock_cursor_free(en->preedit_start); en->preedit_start = NULL; } if (en->preedit_end) { evas_textblock_cursor_free(en->preedit_end); en->preedit_end = NULL; } if (en->txt_obj) { evas_object_del(en->txt_obj); en->txt_obj = NULL; } if (en->txt_style) { evas_textblock_style_free(en->txt_style); en->txt_style = NULL; } if (en->imf_context) { ecore_imf_context_del(en->imf_context); en->imf_context = NULL; } } int main(void) { Ecore_Evas *ee; Evas *evas; Entry en1, en2; if (!ecore_evas_init()) { fprintf(stderr, "failed to call ecore_evas_init()\n"); return EXIT_FAILURE; } ecore_imf_init(); // create a new window, with size=WIDTHxHEIGHT and default engine ee = ecore_evas_new(NULL, 0, 0, WIDTH, HEIGHT, NULL); if (!ee) { fprintf(stderr, "failed to call ecore_evas_new\n"); return EXIT_FAILURE; } ecore_evas_show(ee); // get the canvas off just-created window evas = ecore_evas_get(ee); if (!evas) { fprintf(stderr, "failed to call ecore_evas_get\n"); return EXIT_FAILURE; } // create input field rectangle Evas_Object *bg = evas_object_rectangle_add(evas); evas_object_move(bg, 0, 0); evas_object_resize(bg, WIDTH, HEIGHT); evas_object_color_set(bg, 255, 255, 255, 255); evas_object_show(bg); // register canvas focus in/out event handler evas_event_callback_add(evas, EVAS_CALLBACK_CANVAS_FOCUS_IN, _canvas_focus_in_cb, NULL); evas_event_callback_add(evas, EVAS_CALLBACK_CANVAS_FOCUS_OUT, _canvas_focus_out_cb, NULL); memset(&en1, 0, sizeof(en1)); memset(&en2, 0, sizeof(en2)); // create input field 1 create_input_field(evas, &en1, 40, 60, 400, 80); // create input field 2 create_input_field(evas, &en2, 40, 180, 400, 80); // give focus to input field 1 evas_object_focus_set(en1.rect, EINA_TRUE); ecore_main_loop_begin(); // begin mainloop delete_input_field(&en1); // delete input field 1 delete_input_field(&en2); // delete input field 2 evas_event_callback_del_full(evas, EVAS_CALLBACK_CANVAS_FOCUS_IN, _canvas_focus_in_cb, NULL); evas_event_callback_del_full(evas, EVAS_CALLBACK_CANVAS_FOCUS_OUT, _canvas_focus_out_cb, NULL); ecore_evas_free(ee); ecore_imf_shutdown(); ecore_evas_shutdown(); return 0; }