summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Hirt <daniel.hirt@samsung.com>2016-04-04 11:55:03 +0300
committerDaniel Hirt <daniel.hirt@samsung.com>2016-05-16 16:44:36 +0300
commit61984af564e6550cfc22a05e79970543458443c5 (patch)
treea8bbd1da6429a95fbb1f5572ee667c914d7e2a6b
parent1be9ed9b699f584924811dba8137935a36c4f8ab (diff)
downloadefl-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.c268
-rw-r--r--src/lib/evas/canvas/evas_textblock.eo4
-rw-r--r--src/tests/evas/evas_test_textblock.c87
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