summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorYoungbok Shin <youngb.shin@samsung.com>2018-08-20 07:21:53 -0400
committerMike Blumenkrantz <zmike@samsung.com>2018-08-20 10:29:32 -0400
commit517018e00897f61136418861563a49144a5fe39a (patch)
tree694c299402631380a10a7ad6ddbebfbb774e7e91 /src/lib
parent8da56ac873d5bb083b7cfe08aeefdaa2ad9a4b99 (diff)
evas textblock: add/apply cursor cluster APIs based on grapheme cluster
Summary: Add a feature for moving cursor over a grapheme cluster. It is applied to edje_entry.c and elm_entry.c for improving cursor handling just like other modern text editors. ex) gedit The patch on Evas needs to update libunibreak library. So, the patch will update libunibreak, too. @feature Test Plan: 1. Put "ഹലോ" in your entry. 2. Your cursor can reach at the end of text from the beginning only in 2 right key event with this feature. Reviewers: raster, cedric, jpeg, herdsman, zmike, devilhorns Reviewed By: herdsman, zmike Subscribers: #reviewers, #committers, zmike, bowonryu, woohyun Tags: #efl Differential Revision: https://phab.enlightenment.org/D5490
Diffstat (limited to '')
-rw-r--r--src/lib/edje/edje_entry.c51
-rw-r--r--src/lib/efl/interfaces/efl_text_cursor.eo25
-rw-r--r--src/lib/evas/canvas/efl_canvas_text.eo3
-rw-r--r--src/lib/evas/canvas/evas_object_textblock.c276
-rw-r--r--src/lib/evas/canvas/evas_textblock_legacy.h26
5 files changed, 337 insertions, 44 deletions
diff --git a/src/lib/edje/edje_entry.c b/src/lib/edje/edje_entry.c
index 6c1f8960ff..1ad3c30b65 100644
--- a/src/lib/edje/edje_entry.c
+++ b/src/lib/edje/edje_entry.c
@@ -536,7 +536,7 @@ _curs_jump_line(Evas_Textblock_Cursor *c, Evas_Object *o, Entry *en, int ln)
536 536
537 if (!evas_object_textblock_line_number_geometry_get(o, ln, &lx, &ly, &lw, &lh)) 537 if (!evas_object_textblock_line_number_geometry_get(o, ln, &lx, &ly, &lw, &lh))
538 return EINA_FALSE; 538 return EINA_FALSE;
539 if (evas_textblock_cursor_char_coord_set(c, cx, ly + (lh / 2))) 539 if (evas_textblock_cursor_cluster_coord_set(c, cx, ly + (lh / 2)))
540 return EINA_TRUE; 540 return EINA_TRUE;
541 evas_textblock_cursor_line_set(c, ln); 541 evas_textblock_cursor_line_set(c, ln);
542 if (cx < (lx + (lw / 2))) 542 if (cx < (lx + (lw / 2)))
@@ -1607,24 +1607,33 @@ _delete_emit(Edje *ed, Evas_Textblock_Cursor *c, Entry *en, size_t pos,
1607 ERR("Running very low on memory"); 1607 ERR("Running very low on memory");
1608 return; 1608 return;
1609 } 1609 }
1610 char *tmp = evas_textblock_cursor_content_get(c); 1610 char *tmp = NULL;
1611 1611
1612 info->insert = EINA_FALSE; 1612 info->insert = EINA_FALSE;
1613 if (backspace) 1613 if (backspace)
1614 { 1614 {
1615 info->change.del.start = pos - 1; 1615 info->change.del.start = pos - 1;
1616 info->change.del.end = pos; 1616 info->change.del.end = pos;
1617 tmp = evas_textblock_cursor_content_get(c);
1618 evas_textblock_cursor_char_delete(c);
1617 } 1619 }
1618 else 1620 else
1619 { 1621 {
1620 info->change.del.start = pos + 1; 1622 Evas_Textblock_Cursor *cc = evas_object_textblock_cursor_new(en->rp->object);
1623 evas_textblock_cursor_copy(c, cc);
1624 evas_textblock_cursor_cluster_next(cc);
1625
1626 info->change.del.start = evas_textblock_cursor_pos_get(cc);
1621 info->change.del.end = pos; 1627 info->change.del.end = pos;
1628
1629 tmp = evas_textblock_cursor_range_text_get(c, cc, EVAS_TEXTBLOCK_TEXT_MARKUP);
1630 evas_textblock_cursor_range_delete(c, cc);
1631 evas_textblock_cursor_free(cc);
1622 } 1632 }
1623 1633
1624 info->change.del.content = eina_stringshare_add(tmp); 1634 info->change.del.content = eina_stringshare_add(tmp);
1625 if (tmp) free(tmp); 1635 if (tmp) free(tmp);
1626 1636
1627 evas_textblock_cursor_char_delete(c);
1628 _edje_emit(ed, "entry,changed", en->rp->part->name); 1637 _edje_emit(ed, "entry,changed", en->rp->part->name);
1629 _edje_emit_full(ed, "entry,changed,user", en->rp->part->name, 1638 _edje_emit_full(ed, "entry,changed,user", en->rp->part->name,
1630 info, _free_entry_change_info); 1639 info, _free_entry_change_info);
@@ -1855,7 +1864,7 @@ _edje_key_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
1855 } 1864 }
1856 } 1865 }
1857 } 1866 }
1858 if (evas_textblock_cursor_char_prev(en->cursor)) 1867 if (evas_textblock_cursor_cluster_prev(en->cursor))
1859 ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD; 1868 ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
1860#if defined(__APPLE__) && defined(__MACH__) 1869#if defined(__APPLE__) && defined(__MACH__)
1861 if (altgr) evas_textblock_cursor_word_start(en->cursor); 1870 if (altgr) evas_textblock_cursor_word_start(en->cursor);
@@ -1903,7 +1912,7 @@ _edje_key_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
1903#else 1912#else
1904 if (control) evas_textblock_cursor_word_end(en->cursor); 1913 if (control) evas_textblock_cursor_word_end(en->cursor);
1905#endif 1914#endif
1906 if (evas_textblock_cursor_char_next(en->cursor)) 1915 if (evas_textblock_cursor_cluster_next(en->cursor))
1907 ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD; 1916 ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
1908 if (en->select_allow) 1917 if (en->select_allow)
1909 { 1918 {
@@ -1921,7 +1930,7 @@ _edje_key_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
1921 // del to start of previous word 1930 // del to start of previous word
1922 _sel_start(en->cursor, rp->object, en); 1931 _sel_start(en->cursor, rp->object, en);
1923 1932
1924 evas_textblock_cursor_char_prev(en->cursor); 1933 evas_textblock_cursor_cluster_prev(en->cursor);
1925 evas_textblock_cursor_word_start(en->cursor); 1934 evas_textblock_cursor_word_start(en->cursor);
1926 1935
1927 _sel_preextend(ed, en->cursor, rp->object, en); 1936 _sel_preextend(ed, en->cursor, rp->object, en);
@@ -1961,7 +1970,7 @@ _edje_key_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED,
1961 _sel_start(en->cursor, rp->object, en); 1970 _sel_start(en->cursor, rp->object, en);
1962 1971
1963 evas_textblock_cursor_word_end(en->cursor); 1972 evas_textblock_cursor_word_end(en->cursor);
1964 evas_textblock_cursor_char_next(en->cursor); 1973 evas_textblock_cursor_cluster_next(en->cursor);
1965 1974
1966 _sel_extend(ed, en->cursor, rp->object, en); 1975 _sel_extend(ed, en->cursor, rp->object, en);
1967 1976
@@ -2400,7 +2409,7 @@ _edje_key_up_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, v
2400} 2409}
2401 2410
2402static Evas_Textblock_Cursor * 2411static Evas_Textblock_Cursor *
2403_edje_cursor_char_coord_set(Edje_Real_Part *rp, Evas_Coord canvasx, Evas_Coord canvasy, Evas_Coord *cx, Evas_Coord *cy) 2412_edje_cursor_cluster_coord_set(Edje_Real_Part *rp, Evas_Coord canvasx, Evas_Coord canvasy, Evas_Coord *cx, Evas_Coord *cy)
2404{ 2413{
2405 Entry *en; 2414 Entry *en;
2406 Evas_Coord x, y, lh = 0, cly = 0; 2415 Evas_Coord x, y, lh = 0, cly = 0;
@@ -2432,7 +2441,7 @@ _edje_cursor_char_coord_set(Edje_Real_Part *rp, Evas_Coord canvasx, Evas_Coord c
2432 evas_textblock_cursor_free(line_cur); 2441 evas_textblock_cursor_free(line_cur);
2433 /* No need to check return value if not able to set the char coord Textblock 2442 /* No need to check return value if not able to set the char coord Textblock
2434 * will take care */ 2443 * will take care */
2435 evas_textblock_cursor_char_coord_set(en->cursor, *cx, *cy); 2444 evas_textblock_cursor_cluster_coord_set(en->cursor, *cx, *cy);
2436 2445
2437 return tc; 2446 return tc;
2438} 2447}
@@ -2530,7 +2539,7 @@ _edje_part_mouse_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_
2530 else 2539 else
2531 { 2540 {
2532 evas_textblock_cursor_word_end(en->cursor); 2541 evas_textblock_cursor_word_end(en->cursor);
2533 evas_textblock_cursor_char_next(en->cursor); 2542 evas_textblock_cursor_cluster_next(en->cursor);
2534 } 2543 }
2535 _sel_extend(en->ed, en->cursor, rp->object, en); 2544 _sel_extend(en->ed, en->cursor, rp->object, en);
2536 } 2545 }
@@ -2544,13 +2553,13 @@ _edje_part_mouse_down_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_
2544 evas_textblock_cursor_word_start(en->cursor); 2553 evas_textblock_cursor_word_start(en->cursor);
2545 _sel_start(en->cursor, rp->object, en); 2554 _sel_start(en->cursor, rp->object, en);
2546 evas_textblock_cursor_word_end(en->cursor); 2555 evas_textblock_cursor_word_end(en->cursor);
2547 evas_textblock_cursor_char_next(en->cursor); 2556 evas_textblock_cursor_cluster_next(en->cursor);
2548 _sel_extend(en->ed, en->cursor, rp->object, en); 2557 _sel_extend(en->ed, en->cursor, rp->object, en);
2549 } 2558 }
2550 goto end; 2559 goto end;
2551 } 2560 }
2552 } 2561 }
2553 tc = _edje_cursor_char_coord_set(rp, ev->canvas.x, ev->canvas.y, &cx, &cy); 2562 tc = _edje_cursor_cluster_coord_set(rp, ev->canvas.x, ev->canvas.y, &cx, &cy);
2554 2563
2555 if (dosel) 2564 if (dosel)
2556 { 2565 {
@@ -2670,7 +2679,7 @@ _edje_part_mouse_up_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UN
2670#endif 2679#endif
2671 2680
2672 /* cx cy are unused but needed in mouse down, please bear with it */ 2681 /* cx cy are unused but needed in mouse down, please bear with it */
2673 tc = _edje_cursor_char_coord_set(rp, ev->canvas.x, ev->canvas.y, &cx, &cy); 2682 tc = _edje_cursor_cluster_coord_set(rp, ev->canvas.x, ev->canvas.y, &cx, &cy);
2674 2683
2675 if (en->select_allow) 2684 if (en->select_allow)
2676 { 2685 {
@@ -2748,7 +2757,7 @@ _edje_part_mouse_move_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_
2748 evas_object_geometry_get(rp->object, &x, &y, &w, &h); 2757 evas_object_geometry_get(rp->object, &x, &y, &w, &h);
2749 cx = ev->cur.canvas.x - x; 2758 cx = ev->cur.canvas.x - x;
2750 cy = ev->cur.canvas.y - y; 2759 cy = ev->cur.canvas.y - y;
2751 if (!evas_textblock_cursor_char_coord_set(en->cursor, cx, cy)) 2760 if (!evas_textblock_cursor_cluster_coord_set(en->cursor, cx, cy))
2752 { 2761 {
2753 Evas_Coord lx, ly, lw, lh; 2762 Evas_Coord lx, ly, lw, lh;
2754 2763
@@ -2760,7 +2769,7 @@ _edje_part_mouse_move_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_
2760 { 2769 {
2761 evas_textblock_cursor_paragraph_first(en->cursor); 2770 evas_textblock_cursor_paragraph_first(en->cursor);
2762 evas_textblock_cursor_line_geometry_get(en->cursor, &lx, &ly, &lw, &lh); 2771 evas_textblock_cursor_line_geometry_get(en->cursor, &lx, &ly, &lw, &lh);
2763 if (!evas_textblock_cursor_char_coord_set(en->cursor, cx, ly + (lh / 2))) 2772 if (!evas_textblock_cursor_cluster_coord_set(en->cursor, cx, ly + (lh / 2)))
2764 _curs_end(en->cursor, rp->object, en); 2773 _curs_end(en->cursor, rp->object, en);
2765 } 2774 }
2766 } 2775 }
@@ -4029,7 +4038,7 @@ _edje_text_cursor_next(Edje_Real_Part *rp, Efl_Text_Cursor_Cursor *c)
4029 4038
4030 _edje_entry_imf_context_reset(rp); 4039 _edje_entry_imf_context_reset(rp);
4031 4040
4032 if (!evas_textblock_cursor_char_next(c)) 4041 if (!evas_textblock_cursor_cluster_next(c))
4033 { 4042 {
4034 return EINA_FALSE; 4043 return EINA_FALSE;
4035 } 4044 }
@@ -4062,7 +4071,7 @@ _edje_text_cursor_prev(Edje_Real_Part *rp, Efl_Text_Cursor_Cursor *c)
4062 4071
4063 _edje_entry_imf_context_reset(rp); 4072 _edje_entry_imf_context_reset(rp);
4064 4073
4065 if (!evas_textblock_cursor_char_prev(c)) 4074 if (!evas_textblock_cursor_cluster_prev(c))
4066 { 4075 {
4067 if (evas_textblock_cursor_paragraph_prev(c)) goto ok; 4076 if (evas_textblock_cursor_paragraph_prev(c)) goto ok;
4068 else return EINA_FALSE; 4077 else return EINA_FALSE;
@@ -4106,7 +4115,7 @@ _edje_text_cursor_up(Edje_Real_Part *rp, Efl_Text_Cursor_Cursor *c)
4106 &lx, &ly, &lw, &lh)) 4115 &lx, &ly, &lw, &lh))
4107 return EINA_FALSE; 4116 return EINA_FALSE;
4108 evas_textblock_cursor_char_geometry_get(c, &cx, &cy, &cw, &ch); 4117 evas_textblock_cursor_char_geometry_get(c, &cx, &cy, &cw, &ch);
4109 if (!evas_textblock_cursor_char_coord_set(c, cx, ly + (lh / 2))) 4118 if (!evas_textblock_cursor_cluster_coord_set(c, cx, ly + (lh / 2)))
4110 evas_textblock_cursor_line_char_last(c); 4119 evas_textblock_cursor_line_char_last(c);
4111 _sel_update(en->ed, c, rp->object, rp->typedata.text->entry_data); 4120 _sel_update(en->ed, c, rp->object, rp->typedata.text->entry_data);
4112 4121
@@ -4145,7 +4154,7 @@ _edje_text_cursor_down(Edje_Real_Part *rp, Efl_Text_Cursor_Cursor *c)
4145 &lx, &ly, &lw, &lh)) 4154 &lx, &ly, &lw, &lh))
4146 return EINA_FALSE; 4155 return EINA_FALSE;
4147 evas_textblock_cursor_char_geometry_get(c, &cx, &cy, &cw, &ch); 4156 evas_textblock_cursor_char_geometry_get(c, &cx, &cy, &cw, &ch);
4148 if (!evas_textblock_cursor_char_coord_set(c, cx, ly + (lh / 2))) 4157 if (!evas_textblock_cursor_cluster_coord_set(c, cx, ly + (lh / 2)))
4149 evas_textblock_cursor_line_char_last(c); 4158 evas_textblock_cursor_line_char_last(c);
4150 4159
4151 _sel_update(en->ed, c, rp->object, rp->typedata.text->entry_data); 4160 _sel_update(en->ed, c, rp->object, rp->typedata.text->entry_data);
@@ -4347,7 +4356,7 @@ _edje_text_cursor_coord_set(Edje_Real_Part *rp, Efl_Text_Cursor_Cursor *c,
4347 _edje_emit(en->ed, "selection,changed", rp->part->name); 4356 _edje_emit(en->ed, "selection,changed", rp->part->name);
4348 } 4357 }
4349 } 4358 }
4350 return evas_textblock_cursor_char_coord_set(c, x, y); 4359 return evas_textblock_cursor_cluster_coord_set(c, x, y);
4351} 4360}
4352 4361
4353Eina_Bool 4362Eina_Bool
diff --git a/src/lib/efl/interfaces/efl_text_cursor.eo b/src/lib/efl/interfaces/efl_text_cursor.eo
index 973c7d85bd..b0e4c28b51 100644
--- a/src/lib/efl/interfaces/efl_text_cursor.eo
+++ b/src/lib/efl/interfaces/efl_text_cursor.eo
@@ -145,6 +145,20 @@ interface Efl.Text_Cursor {
145 /* @inout */ cur: ptr(Efl.Text_Cursor_Cursor); [[Cursor object]] 145 /* @inout */ cur: ptr(Efl.Text_Cursor_Cursor); [[Cursor object]]
146 } 146 }
147 } 147 }
148 cursor_cluster_next {
149 [[Advances to the next grapheme cluster]]
150 legacy: null;
151 params {
152 /* @inout */ cur: ptr(Efl.Text_Cursor_Cursor); [[Cursor object]]
153 }
154 }
155 cursor_cluster_prev {
156 [[Advances to the previous grapheme cluster]]
157 legacy: null;
158 params {
159 /* @inout */ cur: ptr(Efl.Text_Cursor_Cursor); [[Cursor object]]
160 }
161 }
148 cursor_paragraph_char_first { 162 cursor_paragraph_char_first {
149 [[Advances to the first character in this paragraph]] 163 [[Advances to the first character in this paragraph]]
150 legacy: null; 164 legacy: null;
@@ -232,6 +246,17 @@ interface Efl.Text_Cursor {
232 @in y: int; [[Y coord to set by.]] 246 @in y: int; [[Y coord to set by.]]
233 } 247 }
234 } 248 }
249 cursor_cluster_coord_set {
250 [[Set cursor coordinates according to grapheme clusters.
251 It does not allow to put a cursor to the middle of a grapheme cluster.
252 ]]
253 legacy: null;
254 params {
255 /* @inout */ cur: ptr(Efl.Text_Cursor_Cursor); [[Cursor object]]
256 @in x: int; [[X coord to set by.]]
257 @in y: int; [[Y coord to set by.]]
258 }
259 }
235 cursor_text_insert { 260 cursor_text_insert {
236 [[Adds text to the current cursor position and set the cursor to 261 [[Adds text to the current cursor position and set the cursor to
237 *after* the start of the text just added. 262 *after* the start of the text just added.
diff --git a/src/lib/evas/canvas/efl_canvas_text.eo b/src/lib/evas/canvas/efl_canvas_text.eo
index b24139e34a..6c82adf82a 100644
--- a/src/lib/evas/canvas/efl_canvas_text.eo
+++ b/src/lib/evas/canvas/efl_canvas_text.eo
@@ -327,6 +327,8 @@ class Efl.Canvas.Text (Efl.Canvas.Object, Efl.Text,
327 Efl.Text_Cursor.cursor_copy; 327 Efl.Text_Cursor.cursor_copy;
328 Efl.Text_Cursor.cursor_char_next; 328 Efl.Text_Cursor.cursor_char_next;
329 Efl.Text_Cursor.cursor_char_prev; 329 Efl.Text_Cursor.cursor_char_prev;
330 Efl.Text_Cursor.cursor_cluster_next;
331 Efl.Text_Cursor.cursor_cluster_prev;
330 Efl.Text_Cursor.cursor_paragraph_char_first; 332 Efl.Text_Cursor.cursor_paragraph_char_first;
331 Efl.Text_Cursor.cursor_paragraph_char_last; 333 Efl.Text_Cursor.cursor_paragraph_char_last;
332 Efl.Text_Cursor.cursor_word_start; 334 Efl.Text_Cursor.cursor_word_start;
@@ -339,6 +341,7 @@ class Efl.Canvas.Text (Efl.Canvas.Object, Efl.Text,
339 Efl.Text_Cursor.cursor_paragraph_prev; 341 Efl.Text_Cursor.cursor_paragraph_prev;
340 Efl.Text_Cursor.cursor_line_jump_by; 342 Efl.Text_Cursor.cursor_line_jump_by;
341 Efl.Text_Cursor.cursor_coord_set; 343 Efl.Text_Cursor.cursor_coord_set;
344 Efl.Text_Cursor.cursor_cluster_coord_set;
342 Efl.Text_Cursor.cursor_text_insert; 345 Efl.Text_Cursor.cursor_text_insert;
343 Efl.Text_Cursor.cursor_char_delete; 346 Efl.Text_Cursor.cursor_char_delete;
344 Efl.Text_Annotate.annotation { set; get; } 347 Efl.Text_Annotate.annotation { set; get; }
diff --git a/src/lib/evas/canvas/evas_object_textblock.c b/src/lib/evas/canvas/evas_object_textblock.c
index f523f131e9..16f717ce41 100644
--- a/src/lib/evas/canvas/evas_object_textblock.c
+++ b/src/lib/evas/canvas/evas_object_textblock.c
@@ -77,6 +77,7 @@
77 77
78#include "linebreak.h" 78#include "linebreak.h"
79#include "wordbreak.h" 79#include "wordbreak.h"
80#include "graphemebreak.h"
80 81
81#include "evas_filter.h" 82#include "evas_filter.h"
82#include "efl_canvas_filter_internal.eo.h" 83#include "efl_canvas_filter_internal.eo.h"
@@ -9271,20 +9272,122 @@ _efl_canvas_text_efl_text_cursor_cursor_word_end(Eo *eo_obj, Efl_Canvas_Text_Dat
9271 efl_event_callback_legacy_call(eo_obj, EFL_CANVAS_TEXT_EVENT_CURSOR_CHANGED, NULL); 9272 efl_event_callback_legacy_call(eo_obj, EFL_CANVAS_TEXT_EVENT_CURSOR_CHANGED, NULL);
9272} 9273}
9273 9274
9274EAPI Eina_Bool 9275static char *
9275evas_textblock_cursor_char_next(Efl_Text_Cursor_Cursor *cur) 9276_evas_textblock_grapheme_breaks_new(Evas_Object_Textblock_Item *it, size_t len)
9276{ 9277{
9277 int ind; 9278 char *grapheme_breaks = NULL;
9279 const char *lang = (it->format->font.fdesc) ? it->format->font.fdesc->lang : "";
9280
9281 grapheme_breaks = malloc(len);
9282 if (!grapheme_breaks) return NULL;
9283
9284 set_graphemebreaks_utf32((const utf32_t *)
9285 eina_ustrbuf_string_get(
9286 it->text_node->unicode),
9287 len, lang, grapheme_breaks);
9288
9289 return grapheme_breaks;
9290}
9291
9292static size_t
9293_evas_textblock_cursor_cluster_pos_get(Evas_Textblock_Cursor *cur, Eina_Bool inc)
9294{
9295 Evas_Object_Textblock_Paragraph *par;
9296 Efl_Canvas_Text_Data *o;
9297 size_t cur_pos = cur->pos;
9298 size_t ret = cur->pos;
9299
9300 if (!inc) cur_pos--;
9301
9302 if (!cur->node->par)
9303 {
9304 o = efl_data_scope_get(cur->obj, MY_CLASS);
9305 if (o) _relayout_if_needed(cur->obj, o);
9306 }
9307
9308 par = cur->node->par;
9309
9310 if (par)
9311 {
9312 Eina_List *l;
9313 Evas_Object_Textblock_Item *it, *last_it = NULL;
9314 EINA_LIST_FOREACH(par->logical_items, l, it)
9315 {
9316 if (it->text_pos > cur_pos)
9317 {
9318 if (!last_it) last_it = it;
9319 break;
9320 }
9321 last_it = it;
9322 }
9323
9324 if (last_it)
9325 {
9326 it = last_it;
9327 if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
9328 {
9329 size_t len = eina_ustrbuf_length_get(it->text_node->unicode);
9330 char *grapheme_breaks = _evas_textblock_grapheme_breaks_new(it, len);
9331
9332 if (grapheme_breaks)
9333 {
9334 size_t grapheme_breaks_index = cur_pos;
9335
9336 if (inc)
9337 {
9338 while ((grapheme_breaks_index < len) &&
9339 (grapheme_breaks[grapheme_breaks_index] != GRAPHEMEBREAK_BREAK))
9340 {
9341 grapheme_breaks_index++;
9342 }
9343
9344 ret = grapheme_breaks_index + 1;
9345 }
9346 else
9347 {
9348 while ((grapheme_breaks_index > 0) &&
9349 (grapheme_breaks[grapheme_breaks_index - 1] != GRAPHEMEBREAK_BREAK))
9350 {
9351 grapheme_breaks_index--;
9352 }
9353
9354 ret = grapheme_breaks_index;
9355 }
9356
9357 free(grapheme_breaks);
9358 }
9359 }
9360 }
9361 }
9362
9363 return ret;
9364}
9365
9366static Eina_Bool
9367_evas_textblock_cursor_next(Evas_Textblock_Cursor *cur, Eina_Bool per_cluster)
9368{
9369 Evas_Object_Protected_Data *obj;
9278 const Eina_Unicode *text; 9370 const Eina_Unicode *text;
9371 int ind;
9279 9372
9280 if (!cur) return EINA_FALSE; 9373 if (!cur) return EINA_FALSE;
9281 Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
9282 evas_object_async_block(obj);
9283 TB_NULL_CHECK(cur->node, EINA_FALSE); 9374 TB_NULL_CHECK(cur->node, EINA_FALSE);
9284 9375
9376 obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
9377 evas_object_async_block(obj);
9378
9285 ind = cur->pos; 9379 ind = cur->pos;
9286 text = eina_ustrbuf_string_get(cur->node->unicode); 9380 text = eina_ustrbuf_string_get(cur->node->unicode);
9287 if (text[ind]) ind++; 9381
9382 if (text[ind])
9383 {
9384 if (per_cluster)
9385 ind = _evas_textblock_cursor_cluster_pos_get(cur, EINA_TRUE);
9386
9387 if (ind <= (int)cur->pos)
9388 ind = cur->pos + 1;
9389 }
9390
9288 /* Only allow pointing a null if it's the last paragraph. 9391 /* Only allow pointing a null if it's the last paragraph.
9289 * because we don't have a PS there. */ 9392 * because we don't have a PS there. */
9290 if (text[ind]) 9393 if (text[ind])
@@ -9311,23 +9414,30 @@ evas_textblock_cursor_char_next(Efl_Text_Cursor_Cursor *cur)
9311 } 9414 }
9312} 9415}
9313 9416
9314EOLIAN static void
9315_efl_canvas_text_efl_text_cursor_cursor_char_next(Eo *eo_obj, Efl_Canvas_Text_Data *o EINA_UNUSED, Efl_Text_Cursor_Cursor *cur)
9316{
9317 ASYNC_BLOCK;
9318 evas_textblock_cursor_char_next(cur);
9319 efl_event_callback_legacy_call(eo_obj, EFL_CANVAS_TEXT_EVENT_CURSOR_CHANGED, NULL);
9320}
9321
9322static Eina_Bool 9417static Eina_Bool
9323_evas_textblock_cursor_char_prev(Efl_Text_Cursor_Cursor *cur) 9418_evas_textblock_cursor_prev(Evas_Textblock_Cursor *cur, Eina_Bool per_cluster)
9324{ 9419{
9420 Evas_Object_Protected_Data *obj;
9325 9421
9326 if (!cur) return EINA_FALSE; 9422 if (!cur) return EINA_FALSE;
9327 TB_NULL_CHECK(cur->node, EINA_FALSE); 9423 TB_NULL_CHECK(cur->node, EINA_FALSE);
9328 9424
9425 obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
9426 evas_object_async_block(obj);
9427
9329 if (cur->pos != 0) 9428 if (cur->pos != 0)
9330 { 9429 {
9430 if (per_cluster)
9431 {
9432 size_t ret = _evas_textblock_cursor_cluster_pos_get(cur, EINA_FALSE);
9433
9434 if (ret != cur->pos)
9435 {
9436 cur->pos = ret;
9437 return EINA_TRUE;
9438 }
9439 }
9440
9331 cur->pos--; 9441 cur->pos--;
9332 return EINA_TRUE; 9442 return EINA_TRUE;
9333 } 9443 }
@@ -9335,18 +9445,59 @@ _evas_textblock_cursor_char_prev(Efl_Text_Cursor_Cursor *cur)
9335} 9445}
9336 9446
9337EAPI Eina_Bool 9447EAPI Eina_Bool
9448evas_textblock_cursor_char_next(Efl_Text_Cursor_Cursor *cur)
9449{
9450 return _evas_textblock_cursor_next(cur, EINA_FALSE);
9451}
9452
9453EOLIAN static void
9454_efl_canvas_text_efl_text_cursor_cursor_char_next(Eo *eo_obj, Efl_Canvas_Text_Data *o EINA_UNUSED, Efl_Text_Cursor_Cursor *cur)
9455{
9456 ASYNC_BLOCK;
9457 if (_evas_textblock_cursor_next(cur, EINA_FALSE))
9458 efl_event_callback_legacy_call(eo_obj, EFL_CANVAS_TEXT_EVENT_CURSOR_CHANGED, NULL);
9459}
9460
9461EAPI Eina_Bool
9338evas_textblock_cursor_char_prev(Efl_Text_Cursor_Cursor *cur) 9462evas_textblock_cursor_char_prev(Efl_Text_Cursor_Cursor *cur)
9339{ 9463{
9340 if (!cur) return EINA_FALSE; 9464 return _evas_textblock_cursor_prev(cur, EINA_FALSE);
9341 return _evas_textblock_cursor_char_prev(cur);
9342} 9465}
9343 9466
9344EOLIAN static void 9467EOLIAN static void
9345_efl_canvas_text_efl_text_cursor_cursor_char_prev(Eo *eo_obj EINA_UNUSED, Efl_Canvas_Text_Data *o EINA_UNUSED, Efl_Text_Cursor_Cursor *cur) 9468_efl_canvas_text_efl_text_cursor_cursor_char_prev(Eo *eo_obj EINA_UNUSED, Efl_Canvas_Text_Data *o EINA_UNUSED, Efl_Text_Cursor_Cursor *cur)
9346{ 9469{
9347 ASYNC_BLOCK; 9470 ASYNC_BLOCK;
9348 _evas_textblock_cursor_char_prev(cur); 9471 if (_evas_textblock_cursor_prev(cur, EINA_FALSE))
9349 efl_event_callback_legacy_call(eo_obj, EFL_CANVAS_TEXT_EVENT_CURSOR_CHANGED, NULL); 9472 efl_event_callback_legacy_call(eo_obj, EFL_CANVAS_TEXT_EVENT_CURSOR_CHANGED, NULL);
9473}
9474
9475EAPI Eina_Bool
9476evas_textblock_cursor_cluster_next(Efl_Text_Cursor_Cursor *cur)
9477{
9478 return _evas_textblock_cursor_next(cur, EINA_TRUE);
9479}
9480
9481EOLIAN static void
9482_efl_canvas_text_efl_text_cursor_cursor_cluster_next(Eo *eo_obj, Efl_Canvas_Text_Data *o EINA_UNUSED, Efl_Text_Cursor_Cursor *cur)
9483{
9484 ASYNC_BLOCK;
9485 if (_evas_textblock_cursor_next(cur, EINA_TRUE))
9486 efl_event_callback_legacy_call(eo_obj, EFL_CANVAS_TEXT_EVENT_CURSOR_CHANGED, NULL);
9487}
9488
9489EAPI Eina_Bool
9490evas_textblock_cursor_cluster_prev(Efl_Text_Cursor_Cursor *cur)
9491{
9492 return _evas_textblock_cursor_prev(cur, EINA_TRUE);
9493}
9494
9495EOLIAN static void
9496_efl_canvas_text_efl_text_cursor_cursor_cluster_prev(Eo *eo_obj, Efl_Canvas_Text_Data *o EINA_UNUSED, Efl_Text_Cursor_Cursor *cur)
9497{
9498 ASYNC_BLOCK;
9499 if (_evas_textblock_cursor_prev(cur, EINA_TRUE))
9500 efl_event_callback_legacy_call(eo_obj, EFL_CANVAS_TEXT_EVENT_CURSOR_CHANGED, NULL);
9350} 9501}
9351 9502
9352EAPI void 9503EAPI void
@@ -12031,15 +12182,16 @@ _efl_canvas_text_visible_range_get(Eo *eo_obj EINA_UNUSED,
12031 return EINA_TRUE; 12182 return EINA_TRUE;
12032} 12183}
12033 12184
12034 12185static Eina_Bool
12035EAPI Eina_Bool 12186_evas_textblock_cursor_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord x, Evas_Coord y, Eina_Bool per_cluster)
12036evas_textblock_cursor_char_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord x, Evas_Coord y)
12037{ 12187{
12038 Evas_Object_Textblock_Paragraph *found_par; 12188 Evas_Object_Textblock_Paragraph *found_par;
12039 Evas_Object_Textblock_Line *ln; 12189 Evas_Object_Textblock_Line *ln;
12040 Evas_Object_Textblock_Item *it = NULL; 12190 Evas_Object_Textblock_Item *it = NULL;
12041 Eina_Bool ret = EINA_FALSE; 12191 Eina_Bool ret = EINA_FALSE;
12042 12192
12193 if (!cur) return ret;
12194
12043 Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS); 12195 Evas_Object_Protected_Data *obj = efl_data_scope_get(cur->obj, EFL_CANVAS_OBJECT_CLASS);
12044 evas_object_async_block(obj); 12196 evas_object_async_block(obj);
12045 Efl_Canvas_Text_Data *o = efl_data_scope_get(cur->obj, MY_CLASS); 12197 Efl_Canvas_Text_Data *o = efl_data_scope_get(cur->obj, MY_CLASS);
@@ -12112,6 +12264,63 @@ evas_textblock_cursor_char_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord x,
12112 &cx, &cy, &cw, &ch); 12264 &cx, &cy, &cw, &ch);
12113 if (pos < 0) 12265 if (pos < 0)
12114 goto end; 12266 goto end;
12267
12268 if ((pos > 0) && per_cluster)
12269 {
12270 size_t len = eina_ustrbuf_length_get(it->text_node->unicode);
12271 char *grapheme_breaks = _evas_textblock_grapheme_breaks_new(it, len);
12272
12273 /* If current position is not breakable,
12274 * try to move cursor to a nearest breakable position. */
12275 if (grapheme_breaks && (grapheme_breaks[pos + it->text_pos - 1] != GRAPHEMEBREAK_BREAK))
12276 {
12277 size_t left_index = pos + it->text_pos - 1;
12278 size_t right_index = pos + it->text_pos - 1;
12279 int lx, rx;
12280
12281 /* To the left */
12282 while ((left_index > 0) &&
12283 (grapheme_breaks[left_index] != GRAPHEMEBREAK_BREAK))
12284 {
12285 left_index--;
12286 }
12287
12288 ENFN->font_pen_coords_get(ENC,
12289 ti->parent.format->font.font,
12290 &ti->text_props,
12291 left_index - it->text_pos + 1,
12292 &lx, NULL, NULL, NULL);
12293
12294 /* To the right */
12295 while ((right_index < len) &&
12296 (grapheme_breaks[right_index] != GRAPHEMEBREAK_BREAK))
12297 {
12298 right_index++;
12299 }
12300
12301 ENFN->font_pen_coords_get(ENC,
12302 ti->parent.format->font.font,
12303 &ti->text_props,
12304 right_index - it->text_pos + 1,
12305 &rx, NULL, NULL, NULL);
12306
12307 /* Decide a nearest position by checking its geometry. */
12308 if (((ti->text_props.bidi_dir != EVAS_BIDI_DIRECTION_RTL) &&
12309 ((ln->x + it->x + rx - x) >= (x - (lx + ln->x + it->x)))) ||
12310 ((ti->text_props.bidi_dir == EVAS_BIDI_DIRECTION_RTL) &&
12311 ((ln->x + it->x + lx - x) >= (x - (rx + ln->x + it->x)))))
12312 {
12313 pos = left_index - it->text_pos + 1;
12314 }
12315 else
12316 {
12317 pos = right_index - it->text_pos + 1;
12318 }
12319 }
12320
12321 free(grapheme_breaks);
12322 }
12323
12115 cur->pos = pos + it->text_pos; 12324 cur->pos = pos + it->text_pos;
12116 cur->node = it->text_node; 12325 cur->node = it->text_node;
12117 ret = EINA_TRUE; 12326 ret = EINA_TRUE;
@@ -12167,6 +12376,18 @@ end:
12167 return ret; 12376 return ret;
12168} 12377}
12169 12378
12379EAPI Eina_Bool
12380evas_textblock_cursor_char_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord x, Evas_Coord y)
12381{
12382 return _evas_textblock_cursor_coord_set(cur, x, y, EINA_FALSE);
12383}
12384
12385EAPI Eina_Bool
12386evas_textblock_cursor_cluster_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord x, Evas_Coord y)
12387{
12388 return _evas_textblock_cursor_coord_set(cur, x, y, EINA_TRUE);
12389}
12390
12170EOLIAN static void 12391EOLIAN static void
12171_efl_canvas_text_efl_text_cursor_cursor_coord_set(Eo *eo_obj EINA_UNUSED, Efl_Canvas_Text_Data *o EINA_UNUSED, Efl_Text_Cursor_Cursor *cur EINA_UNUSED, 12392_efl_canvas_text_efl_text_cursor_cursor_coord_set(Eo *eo_obj EINA_UNUSED, Efl_Canvas_Text_Data *o EINA_UNUSED, Efl_Text_Cursor_Cursor *cur EINA_UNUSED,
12172 Evas_Coord x, Evas_Coord y) 12393 Evas_Coord x, Evas_Coord y)
@@ -12175,6 +12396,14 @@ _efl_canvas_text_efl_text_cursor_cursor_coord_set(Eo *eo_obj EINA_UNUSED, Efl_Ca
12175 evas_textblock_cursor_char_coord_set(cur, x, y); 12396 evas_textblock_cursor_char_coord_set(cur, x, y);
12176} 12397}
12177 12398
12399EOLIAN static void
12400_efl_canvas_text_efl_text_cursor_cursor_cluster_coord_set(Eo *eo_obj EINA_UNUSED, Efl_Canvas_Text_Data *o EINA_UNUSED, Efl_Text_Cursor_Cursor *cur EINA_UNUSED,
12401 Evas_Coord x, Evas_Coord y)
12402{
12403 ASYNC_BLOCK;
12404 evas_textblock_cursor_cluster_coord_set(cur, x, y);
12405}
12406
12178EAPI int 12407EAPI int
12179evas_textblock_cursor_line_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord y) 12408evas_textblock_cursor_line_coord_set(Evas_Textblock_Cursor *cur, Evas_Coord y)
12180{ 12409{
@@ -13279,6 +13508,7 @@ evas_object_textblock_init(Evas_Object *eo_obj)
13279 linebreak_init = EINA_TRUE; 13508 linebreak_init = EINA_TRUE;
13280 init_linebreak(); 13509 init_linebreak();
13281 init_wordbreak(); 13510 init_wordbreak();
13511 init_graphemebreak();
13282 } 13512 }
13283 13513
13284 o = obj->private_data; 13514 o = obj->private_data;
@@ -15194,7 +15424,7 @@ _efl_canvas_text_efl_text_annotate_range_annotations_get(const Eo *eo_obj, Efl_C
15194 if (!it->start_node || !it->end_node) continue; 15424 if (!it->start_node || !it->end_node) continue;
15195 _textblock_cursor_pos_at_fnode_set(eo_obj, &start2, it->start_node); 15425 _textblock_cursor_pos_at_fnode_set(eo_obj, &start2, it->start_node);
15196 _textblock_cursor_pos_at_fnode_set(eo_obj, &end2, it->end_node); 15426 _textblock_cursor_pos_at_fnode_set(eo_obj, &end2, it->end_node);
15197 _evas_textblock_cursor_char_prev(&end2); 15427 evas_textblock_cursor_char_prev(&end2);
15198 if (!((evas_textblock_cursor_compare(&start2, end) > 0) || 15428 if (!((evas_textblock_cursor_compare(&start2, end) > 0) ||
15199 (evas_textblock_cursor_compare(&end2, start) < 0))) 15429 (evas_textblock_cursor_compare(&end2, start) < 0)))
15200 { 15430 {
diff --git a/src/lib/evas/canvas/evas_textblock_legacy.h b/src/lib/evas/canvas/evas_textblock_legacy.h
index 0d84075fa0..2c8ee05f57 100644
--- a/src/lib/evas/canvas/evas_textblock_legacy.h
+++ b/src/lib/evas/canvas/evas_textblock_legacy.h
@@ -605,6 +605,22 @@ EAPI Eina_Bool evas_textblock_cursor_char_prev(Evas_Textblock_Cursor *obj);
605EAPI Eina_Bool evas_textblock_cursor_char_next(Evas_Textblock_Cursor *obj); 605EAPI Eina_Bool evas_textblock_cursor_char_next(Evas_Textblock_Cursor *obj);
606 606
607/** 607/**
608 * @brief Advances the cursor one grapheme cluster backwards.
609 *
610 * @return @c true on success, @c false otherwise.
611 */
612EAPI Eina_Bool evas_textblock_cursor_cluster_prev(Evas_Textblock_Cursor *obj);
613
614/**
615 * @brief Advances the cursor one grapheme cluster forward.
616 *
617 * @return @c true on success, @c false otherwise.
618 *
619 * @ingroup Evas_Textblock_Cursor
620 */
621EAPI Eina_Bool evas_textblock_cursor_cluster_next(Evas_Textblock_Cursor *obj);
622
623/**
608 * @brief Advances to the start of the next text node 624 * @brief Advances to the start of the next text node
609 * 625 *
610 * @return @c true if managed to advance, @c false otherwise 626 * @return @c true if managed to advance, @c false otherwise
@@ -860,6 +876,16 @@ EAPI Evas_Textblock_Cursor *evas_object_textblock_cursor_new(const Evas_Object *
860EAPI Eina_Bool evas_textblock_cursor_char_coord_set(Evas_Textblock_Cursor *obj, Evas_Coord x, Evas_Coord y); 876EAPI Eina_Bool evas_textblock_cursor_char_coord_set(Evas_Textblock_Cursor *obj, Evas_Coord x, Evas_Coord y);
861 877
862/** 878/**
879 * @brief Sets the position of the cursor according to the X and Y coordinates and
880 * grapheme clusters of text.
881 *
882 * @param[in] y y coord to set by.
883 *
884 * @return @c true on success, @c false otherwise.
885 */
886EAPI Eina_Bool evas_textblock_cursor_cluster_coord_set(Evas_Textblock_Cursor *obj, Evas_Coord x, Evas_Coord y);
887
888/**
863 * Free the cursor and unassociate it from the object. 889 * Free the cursor and unassociate it from the object.
864 * @note do not use it to free unassociated cursors. 890 * @note do not use it to free unassociated cursors.
865 * 891 *