diff options
author | Daniel Hirt <daniel.hirt@samsung.com> | 2016-04-04 11:55:03 +0300 |
---|---|---|
committer | Daniel Hirt <daniel.hirt@samsung.com> | 2016-05-16 16:44:36 +0300 |
commit | 61984af564e6550cfc22a05e79970543458443c5 (patch) | |
tree | a8bbd1da6429a95fbb1f5572ee667c914d7e2a6b | |
parent | 1be9ed9b699f584924811dba8137935a36c4f8ab (diff) | |
download | efl-61984af564e6550cfc22a05e79970543458443c5.tar.gz |
Evas textblock: implement Efl.Text
This adds implementations for text.set/get.
There are some notable changes with how Textblock handles input text, that take
effect in the cursor as well as Efl.Text Eo API as follows:
- Paragraphs and lines are broken using inline UTF-8 characters
PARAGRAPH_SEPARATOR and LINE_FEED, respectively. Note that this was
only possible to do with the now-legacy cursor_format_append API.
- Added text now extends the formatting of the previous
character.
-rw-r--r-- | src/lib/evas/canvas/evas_object_textblock.c | 268 | ||||
-rw-r--r-- | src/lib/evas/canvas/evas_textblock.eo | 4 | ||||
-rw-r--r-- | src/tests/evas/evas_test_textblock.c | 87 |
3 files changed, 317 insertions, 42 deletions
diff --git a/src/lib/evas/canvas/evas_object_textblock.c b/src/lib/evas/canvas/evas_object_textblock.c index f599ee9d54..201d60f480 100644 --- a/src/lib/evas/canvas/evas_object_textblock.c +++ b/src/lib/evas/canvas/evas_object_textblock.c @@ -522,6 +522,7 @@ struct _Evas_Object_Textblock } style_pad; double valign; Eina_Stringshare *markup_text; + char *utf8; void *engine_data; const char *repch; const char *bidi_delimiters; @@ -8395,21 +8396,10 @@ evas_textblock_cursor_char_next(Evas_Textblock_Cursor *cur) return evas_obj_textblock_cursor_char_next(cur->obj, cur); } -EOLIAN static Eina_Bool -_evas_textblock_cursor_char_next(Eo *eo_obj, Evas_Textblock_Data *o EINA_UNUSED, - Evas_Textblock_Cursor *cur) +static Eina_Bool +_cursor_next_par_if_needed(Evas_Textblock_Cursor *cur, int ind) { - int ind; - const Eina_Unicode *text; - - if (!cur) return EINA_FALSE; - Evas_Object_Protected_Data *obj = eo_data_scope_get(eo_obj, EVAS_OBJECT_CLASS); - evas_object_async_block(obj); - TB_NULL_CHECK(cur->node, EINA_FALSE); - - ind = cur->pos; - text = eina_ustrbuf_string_get(cur->node->unicode); - if (text[ind]) ind++; + const Eina_Unicode *text = eina_ustrbuf_string_get(cur->node->unicode); /* Only allow pointing a null if it's the last paragraph. * because we don't have a PS there. */ if (text[ind]) @@ -8436,6 +8426,25 @@ _evas_textblock_cursor_char_next(Eo *eo_obj, Evas_Textblock_Data *o EINA_UNUSED, } } +EOLIAN static Eina_Bool +_evas_textblock_cursor_char_next(Eo *eo_obj, Evas_Textblock_Data *o EINA_UNUSED, + Evas_Textblock_Cursor *cur) +{ + int ind; + const Eina_Unicode *text; + + if (!cur) return EINA_FALSE; + Evas_Object_Protected_Data *obj = eo_data_scope_get(eo_obj, EVAS_OBJECT_CLASS); + evas_object_async_block(obj); + TB_NULL_CHECK(cur->node, EINA_FALSE); + + ind = cur->pos; + text = eina_ustrbuf_string_get(cur->node->unicode); + if (text[ind]) ind++; + + return _cursor_next_par_if_needed(cur, ind); +} + EAPI Eina_Bool evas_textblock_cursor_char_prev(Evas_Textblock_Cursor *cur) { @@ -9266,7 +9275,8 @@ _evas_textblock_node_text_new(void) */ static void _evas_textblock_cursor_break_paragraph(Evas_Textblock_Cursor *cur, - Evas_Object_Textblock_Node_Format *fnode) + Evas_Object_Textblock_Node_Format *fnode, + Eina_Bool legacy) { Evas_Object_Textblock_Node_Text *n; @@ -9285,20 +9295,27 @@ _evas_textblock_cursor_break_paragraph(Evas_Textblock_Cursor *cur, size_t len, start; const Eina_Unicode *text; + if (legacy) + { /* If there was a format node in the delete range, * make it our format and update the text_node fields, * otherwise, use the paragraph separator * of the previous paragraph. */ - nnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next); - if (nnode && (nnode->text_node == cur->node)) - { - n->format_node = nnode; - nnode->offset--; /* We don't have to take the replacement char - into account anymore */ - while (nnode && (nnode->text_node == cur->node)) + nnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next); + if (nnode && (nnode->text_node == cur->node)) + { + n->format_node = nnode; + nnode->offset--; /* We don't have to take the replacement char + into account anymore */ + while (nnode && (nnode->text_node == cur->node)) + { + nnode->text_node = n; + nnode = _NODE_FORMAT(EINA_INLIST_GET(nnode)->next); + } + } + else { - nnode->text_node = n; - nnode = _NODE_FORMAT(EINA_INLIST_GET(nnode)->next); + n->format_node = fnode; } } else @@ -9306,8 +9323,12 @@ _evas_textblock_cursor_break_paragraph(Evas_Textblock_Cursor *cur, n->format_node = fnode; } - /* cur->pos now points to the PS, move after. */ - start = cur->pos + 1; + start = cur->pos; + if (legacy) + { + /* cur->pos now points to the PS, move after. */ + start++; + } len = eina_ustrbuf_length_get(cur->node->unicode) - start; if (len > 0) { @@ -9317,7 +9338,7 @@ _evas_textblock_cursor_break_paragraph(Evas_Textblock_Cursor *cur, cur->node->dirty = EINA_TRUE; } } - else + else if (!cur->node) { fnode = o->format_nodes; if (fnode) @@ -9452,26 +9473,27 @@ _evas_textblock_invalidate_all(Evas_Textblock_Data *o) } } -EOLIAN static int -_evas_textblock_cursor_text_append(Eo *eo_obj, - Evas_Textblock_Data *o EINA_UNUSED, Evas_Textblock_Cursor *cur, - const char *_text) +static int +_cursor_text_append(Eo *eo_obj, Evas_Textblock_Cursor *cur, const char *_text, + Eina_Bool legacy) { Evas_Object_Textblock_Node_Text *n; Evas_Object_Textblock_Node_Format *fnode = NULL; - Eina_Unicode *text; + Eina_Unicode *text, *orig_text; int len = 0; if (!cur) return 0; Evas_Object_Protected_Data *obj = eo_data_scope_get(eo_obj, EVAS_OBJECT_CLASS); evas_object_async_block(obj); text = eina_unicode_utf8_to_unicode(_text, &len); + orig_text = text = eina_unicode_utf8_to_unicode(_text, &len); + Evas_Textblock_Data *o = eo_data_scope_get(cur->obj, MY_CLASS); n = cur->node; if (n) { Evas_Object_Textblock_Node_Format *nnode; - fnode = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur, EINA_TRUE); + fnode = _evas_textblock_cursor_node_format_before_or_at_pos_get(cur, legacy); fnode = _evas_textblock_node_format_last_at_off(fnode); /* find the node after the current in the same paragraph * either we find one and then take the next, or we try to get @@ -9510,7 +9532,10 @@ _evas_textblock_cursor_text_append(Eo *eo_obj, cur->node = n; } - eina_ustrbuf_insert_length(n->unicode, text, len, cur->pos); + size_t pos = cur->pos; + Evas_Object_Textblock_Node_Text *nn = NULL; + eina_ustrbuf_insert_length(n->unicode, text, eina_unicode_strlen(text), pos); + /* Advance the formats */ if (fnode && (fnode->text_node == cur->node)) fnode->offset += len; @@ -9518,19 +9543,105 @@ _evas_textblock_cursor_text_append(Eo *eo_obj, /* Update all the cursors after our position. */ _evas_textblock_cursors_update_offset(cur, cur->node, cur->pos, len); + if (nn) + { + cur->node = nn; + len = eina_unicode_strlen(text); + } _evas_textblock_changed(o, cur->obj); n->dirty = EINA_TRUE; - free(text); + free(orig_text); if (!o->cursor->node) o->cursor->node = o->text_nodes; return len; } +static int +_cursor_text_prepend(Eo *eo_obj, Evas_Textblock_Cursor *cur, const char *_text, + Eina_Bool legacy) +{ + int len; + /*append is essentially prepend without advancing */ + if (!cur) return 0; + Evas_Object_Protected_Data *obj = eo_data_scope_get(eo_obj, EVAS_OBJECT_CLASS); + evas_object_async_block(obj); + len = _cursor_text_append(eo_obj, cur, _text, legacy); + if (len == 0) return 0; + cur->pos += len; /*Advance */ + return len; +} + +static int +_prepend_text_run2(Evas_Textblock_Cursor *cur, const char *s, const char *p) +{ + if ((s) && (p > s)) + { + char *ts; + + ts = alloca(p - s + 1); + strncpy(ts, s, p - s); + ts[p - s] = 0; + return _cursor_text_prepend(cur->obj, cur, ts, EINA_FALSE); + } + return 0; +} + EAPI int evas_textblock_cursor_text_append(Evas_Textblock_Cursor *cur, const char *_text) { - return evas_obj_textblock_cursor_text_append(cur->obj, cur, _text); + return _cursor_text_append(cur->obj, cur, _text, EINA_TRUE); +} + +EOLIAN static int +_evas_textblock_cursor_text_append(Eo *eo_obj EINA_UNUSED, + Evas_Textblock_Data *o EINA_UNUSED, Evas_Textblock_Cursor *cur, + const char *text) +{ + + if (!text) return 0; + + const char *off = text; + int len = 0; + + /* We make use of prepending the cursor, but this needs to return the current + * position's cursor, so we use a temporary one. */ + Evas_Textblock_Cursor *cur2 = evas_object_textblock_cursor_new (cur->obj); + evas_textblock_cursor_copy(cur, cur2); //cur --> cur2 + + Evas_Object_Protected_Data *obj = eo_data_scope_get(eo_obj, EVAS_OBJECT_CLASS); + evas_object_async_block(obj); + + while (*off) + { + char *format = NULL; + int n = 1; + if (!strncmp(_PARAGRAPH_SEPARATOR_UTF8, off, + strlen(_PARAGRAPH_SEPARATOR_UTF8))) + { + format = "ps"; + n = strlen(_PARAGRAPH_SEPARATOR_UTF8); + } + else if (!strncmp(_NEWLINE_UTF8, off, strlen(_NEWLINE_UTF8))) + { + format = "br"; + n = strlen(_NEWLINE_UTF8); + } + + if (format) + { + len += _prepend_text_run2(cur2, text, off); + if (evas_textblock_cursor_format_prepend(cur2, format)) + { + len++; + } + text = off + n; /* sync text with next segment */ + } + off += n; + } + len += _prepend_text_run2(cur2, text, off); + evas_textblock_cursor_free(cur2); + return len; } EOLIAN static int @@ -9538,22 +9649,26 @@ _evas_textblock_cursor_text_prepend(Eo *eo_obj, Evas_Textblock_Data *o EINA_UNUSED, Evas_Textblock_Cursor *cur, const char *_text) { - int len; + int ind, len; /*append is essentially prepend without advancing */ if (!cur) return 0; Evas_Object_Protected_Data *obj = eo_data_scope_get(eo_obj, EVAS_OBJECT_CLASS); evas_object_async_block(obj); - len = evas_textblock_cursor_text_append(cur, _text); + len = evas_obj_textblock_cursor_text_append(eo_obj, cur, _text); if (len == 0) return 0; - cur->pos += len; /*Advance */ + ind = cur->pos + len; /*Advance */ + + /* Duplication a small check from cursor_char_next here: */ + _cursor_next_par_if_needed(cur, ind); return len; } EAPI int evas_textblock_cursor_text_prepend(Evas_Textblock_Cursor *cur, const char *_text) { - return evas_obj_textblock_cursor_text_prepend(cur->obj, cur, _text); + return _cursor_text_prepend(cur->obj, cur, _text, EINA_TRUE); } + /** * @internal * Free a format node @@ -9835,7 +9950,7 @@ _evas_textblock_cursor_format_append(Evas_Textblock_Cursor *cur, _evas_textblock_cursors_update_offset(cur, cur->node, cur->pos, 1); if (_IS_PARAGRAPH_SEPARATOR(o, format)) { - _evas_textblock_cursor_break_paragraph(cur, n); + _evas_textblock_cursor_break_paragraph(cur, n, EINA_TRUE); } else { @@ -13509,6 +13624,77 @@ _evas_textblock_object_item_insert(Eo *eo_obj EINA_UNUSED, return ret; } +EOLIAN static void +_evas_textblock_efl_text_text_set(Eo *eo_obj, Evas_Textblock_Data *o EINA_UNUSED, + const char *text) +{ + Evas_Textblock_Cursor *cur; + + evas_object_textblock_text_markup_set(eo_obj, ""); + cur = evas_obj_textblock_cursor_new(eo_obj); + evas_obj_textblock_cursor_text_append(eo_obj, cur, text); +} + +EOLIAN static const char * +_evas_textblock_efl_text_text_get(Eo *eo_obj EINA_UNUSED, Evas_Textblock_Data *o) +{ + Evas_Object_Protected_Data *obj = eo_data_scope_get(eo_obj, EVAS_OBJECT_CLASS); + evas_object_async_block(obj); + + Evas_Object_Textblock_Node_Text *node; + char *utf8, *off; + + struct + { + char *utf8; + int len; + } *en; + Eina_List *lst_utf8 = NULL; + Eina_List *i; + + int len = 0; + + //XXX: not efficient atm. This value will be cached properly so in the + // meantime the utf8 field will be cleared each call. + if (o->utf8) + { + free(o->utf8); + o->utf8 = NULL; + } + + // XXX: won't use cursor_paragraph_text_get as it's inefficient + // for this function (gets ranges, uses strbuf). + + // gets utf8s and calc length + EINA_INLIST_FOREACH(o->text_nodes, node) + { + en = malloc(sizeof(*en)); + en->utf8 = eina_unicode_unicode_to_utf8(eina_ustrbuf_string_get(node->unicode), &en->len); + lst_utf8 = eina_list_append(lst_utf8, en); + len += en->len; + } + + utf8 = malloc(len + 1); // with terminating '/0' + if (!utf8) goto end; + + off = utf8; + EINA_LIST_FOREACH(lst_utf8, i, en) + { + memcpy(off, en->utf8, en->len); + off += en->len; + } + utf8[len] = '\0'; + o->utf8 = utf8; + +end: + EINA_LIST_FREE(lst_utf8, en) + { + free(en); + } + + return o->utf8; +} + /** * @} */ diff --git a/src/lib/evas/canvas/evas_textblock.eo b/src/lib/evas/canvas/evas_textblock.eo index 072e0b52eb..0fc21ad924 100644 --- a/src/lib/evas/canvas/evas_textblock.eo +++ b/src/lib/evas/canvas/evas_textblock.eo @@ -22,7 +22,7 @@ struct Evas_Textblock_Rectangle h: Evas.Coord; [[rectangle's height.]] } -class Evas.Textblock (Evas.Object) +class Evas.Textblock (Evas.Object, Efl.Text) { legacy_prefix: evas_object_textblock; eo_prefix: evas_obj_textblock; @@ -760,5 +760,7 @@ class Evas.Textblock (Evas.Object) Eo.Base.dbg_info_get; Evas.Object.paragraph_direction.set; Evas.Object.paragraph_direction.get; + Efl.Text.text.set; + Efl.Text.text.get; } } diff --git a/src/tests/evas/evas_test_textblock.c b/src/tests/evas/evas_test_textblock.c index 5d8c410677..610f746885 100644 --- a/src/tests/evas/evas_test_textblock.c +++ b/src/tests/evas/evas_test_textblock.c @@ -4111,6 +4111,92 @@ START_TEST(evas_textblock_annotation) _test_check_annotation(tb, 0, 5, _COMP_PARAMS()); } + /* Using annotations with new text API */ + efl_text_set(tb, "hello"); + an = evas_object_textblock_annotation_insert(tb, 0, 4, "color=#fff"); + _test_check_annotation(tb, 3, 3, _COMP_PARAMS("color=#fff")); + evas_textblock_cursor_pos_set(cur, 5); + /* Old API */ + evas_textblock_cursor_text_append(cur, "a"); + _test_check_annotation(tb, 0, 0, _COMP_PARAMS("color=#fff")); + _test_check_annotation(tb, 5, 5, _COMP_PARAMS()); + /* New API */ + evas_object_textblock_cursor_text_append(tb, cur, "a"); + _test_check_annotation(tb, 0, 0, _COMP_PARAMS("color=#fff")); + _test_check_annotation(tb, 5, 5, _COMP_PARAMS("color=#fff")); + + END_TB_TEST(); +} +END_TEST; + +START_TEST(evas_textblock_text_iface) +{ + START_TB_TEST(); + Evas_Coord nw, nh; + Evas_Coord bw, bh; + Evas_Coord w, h; + const char *utf8; + + efl_text_set(tb, "hello world"); + evas_object_textblock_size_native_get(tb, &bw, &bh); + efl_text_set(tb, "hello\nworld"); + evas_object_textblock_size_native_get(tb, &nw, &nh); + ck_assert_int_gt(nh, bh); + ck_assert_int_gt(bw, nw); + efl_text_set(tb, "hello\nworld\ngoodbye\nworld"); + evas_object_textblock_size_native_get(tb, &nw, &nh); + ck_assert_int_ge(nh, bh * 3); + + /* text_insert */ + + /* text_remove */ + efl_text_set(tb, "a"); + evas_object_textblock_size_native_get(tb, &bw, &bh); + efl_text_set(tb, "a\nb"); + evas_obj_textblock_cursor_pos_set(tb, cur, 1); + evas_obj_textblock_cursor_char_delete(tb, cur); + evas_object_textblock_size_native_get(tb, &nw, &nh); + ck_assert_int_eq(nh, bh); + + efl_text_set(tb, "d"); + evas_object_textblock_size_native_get(tb, &w, &h); + efl_text_set(tb, "aa\nb\nc\nd"); + evas_object_textblock_size_native_get(tb, &bw, &bh); + evas_obj_textblock_cursor_pos_set(tb, cur, 0); + evas_obj_textblock_cursor_char_delete(tb, cur); // delete 'a' + evas_object_textblock_size_native_get(tb, &nw, &nh); + ck_assert_int_eq(nh, bh); + evas_obj_textblock_cursor_char_delete(tb, cur); // delete 'a' + evas_obj_textblock_cursor_char_delete(tb, cur); // delete '\n' + evas_object_textblock_size_native_get(tb, &nw, &nh); + ck_assert_int_lt(nh, bh); + /* "b\nc\nd" is left */ + evas_object_textblock_cursor_char_delete(tb, cur); // b + evas_object_textblock_cursor_char_delete(tb, cur); // \n + evas_object_textblock_cursor_char_delete(tb, cur); // c + evas_object_textblock_cursor_char_delete(tb, cur); // \n + /* expecting "d" only */ + evas_object_textblock_size_native_get(tb, &nw, &nh); + ck_assert_int_eq(nh, h); + ck_assert_int_eq(nw, w); + + /* Text get */ + utf8 = "a"; + efl_text_set(tb, utf8); + ck_assert_str_eq(utf8, efl_text_get(tb)); + utf8 = "a\nb"; + efl_text_set(tb, utf8); + ck_assert_str_eq(utf8, efl_text_get(tb)); + utf8 = "a\u2029b"; + efl_text_set(tb, utf8); + ck_assert_str_eq(utf8, efl_text_get(tb)); + utf8 = "a\u2029bc\ndef\n\u2029"; + efl_text_set(tb, utf8); + ck_assert_str_eq(utf8, efl_text_get(tb)); + utf8 = "\u2029\n\n\n\n\u2029\n\u2029\n\n\n"; + efl_text_set(tb, utf8); + ck_assert_str_eq(utf8, efl_text_get(tb)); + END_TB_TEST(); } END_TEST; @@ -4138,6 +4224,7 @@ void evas_test_textblock(TCase *tc) tcase_add_test(tc, evas_textblock_delete); tcase_add_test(tc, evas_textblock_obstacle); tcase_add_test(tc, evas_textblock_annotation); + tcase_add_test(tc, evas_textblock_text_iface); #ifdef HAVE_HYPHEN tcase_add_test(tc, evas_textblock_hyphenation); #endif |