From 7a05d8d76997399bb55893d4b456b24e65a223cc Mon Sep 17 00:00:00 2001 From: Ali Alzyod Date: Thu, 5 Sep 2019 14:52:51 +0900 Subject: [PATCH] elm_entry: handle cursor delete/backspace with clusters consist of one or multible glyphs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Cluster consist of one glyph, expected to be removed on backspace or delete key. Cluster consist of multible glyph, expectd to remove part of on backspace or delete key. This is behaviour founded in Android. (our current way of handling similar to Qt) **New Behaviour** {F3750386} **Old Behaviour** {F3750387} Test Plan: Auto Testing is challenging because there are no easy way to emulate keyboard down on elm_entry ``` #include EAPI_MAIN int elm_main(int argc, char **argv) { Evas_Object *win,*box,*entry; elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED); win = elm_win_util_standard_add("", ""); elm_win_autodel_set(win, EINA_TRUE); box = elm_box_add(win); entry = elm_entry_add(box); evas_object_size_hint_weight_set(box,EVAS_HINT_EXPAND,EVAS_HINT_EXPAND); evas_object_size_hint_align_set(box,EVAS_HINT_FILL,EVAS_HINT_FILL); elm_entry_entry_set(entry,"🇧🇬อั🇧🇬อั😀😀😀😀😀อั"); evas_object_size_hint_weight_set(entry,EVAS_HINT_EXPAND,0.9); evas_object_size_hint_align_set(entry,EVAS_HINT_FILL,EVAS_HINT_FILL); evas_object_show(entry); evas_object_show(box); elm_box_pack_end(box,entry); elm_win_resize_object_add(win,box); evas_object_resize(win,320,480); evas_object_size_hint_weight_set(entry,EVAS_HINT_EXPAND,0.1); evas_object_size_hint_align_set(entry,EVAS_HINT_FILL,EVAS_HINT_FILL); evas_object_show(win); elm_run(); return 0; } ELM_MAIN() ``` Reviewers: tasn, woohyun, bowonryu Subscribers: cedric, #reviewers, #committers Tags: #efl Differential Revision: https://phab.enlightenment.org/D9628 --- src/lib/edje/edje_entry.c | 52 +++++++++++--- src/lib/evas/Evas_Internal.h | 11 +++ src/lib/evas/canvas/evas_object_textblock.c | 76 ++++++++++++++++++++- 3 files changed, 128 insertions(+), 11 deletions(-) diff --git a/src/lib/edje/edje_entry.c b/src/lib/edje/edje_entry.c index 2363c4b2d3..a15bd64678 100644 --- a/src/lib/edje/edje_entry.c +++ b/src/lib/edje/edje_entry.c @@ -1600,11 +1600,23 @@ static void _delete_emit(Edje *ed, Evas_Textblock_Cursor *c, Entry *en, size_t pos, Eina_Bool backspace) { - if (!evas_textblock_cursor_char_next(c)) + + if (backspace) { - return; + if (!evas_textblock_cursor_char_prev(c)) + { + return; + } + evas_textblock_cursor_char_next(c); + } + else + { + if (!evas_textblock_cursor_char_next(c)) + { + return; + } + evas_textblock_cursor_char_prev(c); } - evas_textblock_cursor_char_prev(c); Edje_Entry_Change_Info *info = calloc(1, sizeof(*info)); if (!info) @@ -1617,16 +1629,40 @@ _delete_emit(Edje *ed, Evas_Textblock_Cursor *c, Entry *en, size_t pos, info->insert = EINA_FALSE; if (backspace) { - info->change.del.start = pos - 1; + + Evas_Textblock_Cursor *cc = evas_object_textblock_cursor_new(en->rp->object); + evas_textblock_cursor_copy(c, cc); + Eina_Bool remove_cluster = evas_textblock_cursor_at_cluster_as_single_glyph(cc,EINA_FALSE); + if (remove_cluster) + { + evas_textblock_cursor_cluster_prev(cc); + } + else + { + evas_textblock_cursor_char_prev(cc); + } + + info->change.del.start = evas_textblock_cursor_pos_get(cc); info->change.del.end = pos; - tmp = evas_textblock_cursor_content_get(c); - evas_textblock_cursor_char_delete(c); + + tmp = evas_textblock_cursor_range_text_get(c, cc, EVAS_TEXTBLOCK_TEXT_MARKUP); + evas_textblock_cursor_range_delete(c, cc); + evas_textblock_cursor_free(cc); } else { Evas_Textblock_Cursor *cc = evas_object_textblock_cursor_new(en->rp->object); evas_textblock_cursor_copy(c, cc); - evas_textblock_cursor_cluster_next(cc); + + Eina_Bool remove_cluster = evas_textblock_cursor_at_cluster_as_single_glyph(cc,EINA_TRUE); + if (remove_cluster) + { + evas_textblock_cursor_cluster_next(cc); + } + else + { + evas_textblock_cursor_char_next(cc); + } info->change.del.start = evas_textblock_cursor_pos_get(cc); info->change.del.end = pos; @@ -1954,7 +1990,7 @@ _edje_key_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, } else { - if (evas_textblock_cursor_char_prev(en->cursor)) + //if (evas_textblock_cursor_char_prev(en->cursor)) { _delete_emit(ed, en->cursor, en, old_cur_pos, EINA_TRUE); } diff --git a/src/lib/evas/Evas_Internal.h b/src/lib/evas/Evas_Internal.h index 2958977275..15e4ed1e80 100644 --- a/src/lib/evas/Evas_Internal.h +++ b/src/lib/evas/Evas_Internal.h @@ -148,6 +148,17 @@ EWAPI extern const Efl_Event_Description _EFL_ANIMATION_PLAYER_EVENT_PRE_STARTED #define EFL_ANIMATION_PLAYER_EVENT_PRE_STARTED (&(_EFL_ANIMATION_PLAYER_EVENT_PRE_STARTED)) /* Efl.Animation.Player END */ + + +/*TextBlock Internal function*/ +/** + * Check if cursor is at cluster with one glyph (replace codepoints with new codepoint). + * + * @param cur the cursor. + * @param forward if Eina_True check cluster after cusror position, else before cursor position. + */ +EAPI Eina_Bool evas_textblock_cursor_at_cluster_as_single_glyph(Evas_Textblock_Cursor *cur,Eina_Bool forward); + #ifdef __cplusplus } #endif diff --git a/src/lib/evas/canvas/evas_object_textblock.c b/src/lib/evas/canvas/evas_object_textblock.c index 5f8ae177fc..90561f4f90 100644 --- a/src/lib/evas/canvas/evas_object_textblock.c +++ b/src/lib/evas/canvas/evas_object_textblock.c @@ -9662,7 +9662,7 @@ _evas_textblock_grapheme_breaks_new(Evas_Object_Textblock_Item *it, size_t len) } static size_t -_evas_textblock_cursor_cluster_pos_get(Evas_Textblock_Cursor *cur, Eina_Bool inc) +_evas_textblock_cursor_cluster_pos_get(Evas_Textblock_Cursor *cur, Eina_Bool inc, Eina_Bool *is_single_glyph) { Evas_Object_Textblock_Paragraph *par; Efl_Canvas_Text_Data *o; @@ -9728,6 +9728,64 @@ _evas_textblock_cursor_cluster_pos_get(Evas_Textblock_Cursor *cur, Eina_Bool inc free(grapheme_breaks); } + + if (is_single_glyph) + { + Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(last_it); + Evas_Text_Props_Info *info = ti->text_props.info; + int it_index = ((inc) ? cur->pos : ret) - last_it->text_pos; + + Evas_Font_OT_Info ot; + if (ti->text_props.len != ti->text_props.text_len)/*if code point count same as glyph count skip it*/ + { + Evas_BiDi_Direction itdir = ti->text_props.bidi_dir; + int i = 0; + if (itdir == EFL_TEXT_BIDIRECTIONAL_TYPE_RTL) + { + for (i = ti->text_props.len-1 ; i >= 0; i--) + { + ot = info->ot[i]; + if (ot.source_cluster >= (size_t)it_index) + break; + } + if (i <= 0) + { + if (ti->text_props.text_len - ot.source_cluster >= 2) + *is_single_glyph = EINA_TRUE; + } + else + { + Evas_Font_OT_Info ot_next = info->ot[i - 1]; + if (ot_next.source_cluster - ot.source_cluster >= 2) + *is_single_glyph = EINA_TRUE; + } + } + else + { + for (i = 0; i < (int) ti->text_props.len; i++) + { + ot = info->ot[i]; + if ((int)ot.source_cluster >= it_index) + break; + } + if ((i + 1) >= (int) ti->text_props.len) + { + if (ti->text_props.text_len - ot.source_cluster >= 2) + *is_single_glyph = EINA_TRUE; + } + else + { + Evas_Font_OT_Info ot_next = info->ot[i + 1]; + if (ot_next.source_cluster - ot.source_cluster >= 2) + *is_single_glyph = EINA_TRUE; + } + } + } + else + { + is_single_glyph = EINA_FALSE; + } + } } } } @@ -9735,6 +9793,18 @@ _evas_textblock_cursor_cluster_pos_get(Evas_Textblock_Cursor *cur, Eina_Bool inc return ret; } +EAPI Eina_Bool evas_textblock_cursor_at_cluster_as_single_glyph(Evas_Textblock_Cursor *cur,Eina_Bool forward) +{ + Eina_Bool is_single_glyph = EINA_FALSE; + size_t ret = _evas_textblock_cursor_cluster_pos_get(cur, forward, &is_single_glyph); + + if ((abs(ret-cur->pos) > 1) && is_single_glyph) + { + return EINA_TRUE; + } + return EINA_FALSE; +} + static Eina_Bool _evas_textblock_cursor_next(Evas_Textblock_Cursor *cur, Eina_Bool per_cluster) { @@ -9754,7 +9824,7 @@ _evas_textblock_cursor_next(Evas_Textblock_Cursor *cur, Eina_Bool per_cluster) if (text[ind]) { if (per_cluster) - ind = _evas_textblock_cursor_cluster_pos_get(cur, EINA_TRUE); + ind = _evas_textblock_cursor_cluster_pos_get(cur, EINA_TRUE, NULL); if (ind <= (int)cur->pos) ind = cur->pos + 1; @@ -9801,7 +9871,7 @@ _evas_textblock_cursor_prev(Evas_Textblock_Cursor *cur, Eina_Bool per_cluster) { if (per_cluster) { - size_t ret = _evas_textblock_cursor_cluster_pos_get(cur, EINA_FALSE); + size_t ret = _evas_textblock_cursor_cluster_pos_get(cur, EINA_FALSE, NULL); if (ret != cur->pos) {