From 8d1a8f0f64d8dea36070e676470247bd14b13a6f Mon Sep 17 00:00:00 2001 From: Daniel Hirt Date: Tue, 29 Mar 2016 16:53:41 +0300 Subject: Evas textblock: add annotation API The annotation API allows applying a format to a range in the text. Consider the following sequence of calls: evas_object_textblock_text_markup_set(tb, "hello world"); evas_object_textblock_annotation_set(tb, 0, 2, "color=#0ff"); evas_object_textblock_annotation_set(tb, 5, 6, "font_weight=Bold"); The above is the equivalent of the call: evas_object_textblock_text_markup_set(tb, "hello" " world"); @feature --- src/lib/evas/canvas/evas_object_textblock.c | 107 ++++++++++++++++++++++++++++ src/lib/evas/canvas/evas_textblock.eo | 37 ++++++++++ src/tests/evas/evas_test_textblock.c | 52 ++++++++++++++ 3 files changed, 196 insertions(+) diff --git a/src/lib/evas/canvas/evas_object_textblock.c b/src/lib/evas/canvas/evas_object_textblock.c index a30ca1366f..0cf1b3f87b 100644 --- a/src/lib/evas/canvas/evas_object_textblock.c +++ b/src/lib/evas/canvas/evas_object_textblock.c @@ -9542,6 +9542,20 @@ _evas_textblock_cursor_is_at_the_end(const Evas_Textblock_Cursor *cur) EINA_TRUE : EINA_FALSE; } +#warning remove this +#if 0 +static void +_nodes_format_print_all(Evas_Object_Textblock_Node_Text *node) +{ + Evas_Textblock_Node_Format *fnode; + printf("Text Node %p:\n", node); + EINA_INLIST_FOREACH(node->format_node, fnode) + { + printf("-- Format Node %p (Text Node: %p): offset=%lu \n", fnode, fnode->text_node, fnode->offset); + } +} +#endif + EAPI Eina_Bool evas_textblock_cursor_format_append(Evas_Textblock_Cursor *cur, const char *format) { @@ -9674,6 +9688,10 @@ evas_textblock_cursor_format_append(Evas_Textblock_Cursor *cur, const char *form if (!o->cursor->node) o->cursor->node = o->text_nodes; +#warning remove this +#if 0 + _nodes_format_print_all(cur->node); +#endif return is_visible; } @@ -12846,6 +12864,95 @@ _evas_textblock_evas_object_paragraph_direction_get(Eo *eo_obj EINA_UNUSED, return o->paragraph_direction; } +/* Annotation API - WIP */ +EOLIAN static Eina_Bool +_evas_textblock_annotation_set(Eo *eo_obj, Evas_Textblock_Data *o, + int start, int end EINA_UNUSED, char *format) +{ + int len; + char *buf; + Evas_Textblock_Cursor *cur; + + if (!format || (format[0] == '\0')) return EINA_TRUE; + + if ((end >= 0) && (end <= start)) return EINA_FALSE; + cur = evas_obj_textblock_cursor_new(eo_obj); + + /* Add opening format at 'start' */ + evas_textblock_cursor_pos_set(cur, start); + len = strlen(format); + buf = malloc(len + 3); + sprintf(buf, "<%s>", format); + evas_textblock_cursor_format_append(cur, buf); + free(buf); + + /* Only close the format if (end >= 0) */ + if (end >= 0) + { + evas_textblock_cursor_pos_set(cur, end + 1); + len = strlen(format); + buf = malloc(len + 4); + sprintf(buf, "", format); + evas_textblock_cursor_format_append(cur, buf); + free(buf); + } + evas_textblock_cursor_free(cur); + _evas_textblock_changed(o, eo_obj); + + return EINA_TRUE; +} + +EOLIAN static char * +_evas_textblock_annotation_get(Eo *eo_obj EINA_UNUSED, Evas_Textblock_Data *o EINA_UNUSED, + int start EINA_UNUSED, int end EINA_UNUSED) +{ + const Evas_Object_Textblock_Node_Text *node; + const Evas_Object_Textblock_Node_Format *itr; + size_t npos, pos; + char *ret; + Eina_Bool first = EINA_TRUE; + + Evas_Textblock_Cursor *cur = evas_object_textblock_cursor_new(eo_obj); + Eina_Strbuf *buf = eina_strbuf_new(); + + evas_textblock_cursor_pos_set(cur, start); + + node = cur->node; + pos = npos = start - cur->pos; + EINA_INLIST_FOREACH(cur->node->format_node, itr) + { + while (itr->text_node != node) + { + npos += eina_ustrbuf_length_get(node->unicode); + node = _NODE_TEXT(EINA_INLIST_GET(node)->next); + pos = npos; + } + pos += itr->offset; + if (pos > (size_t) end) break; + if (pos >= (size_t) start) + { + if (!first) + { + eina_strbuf_append(buf, " "); + } + else + { + first = EINA_FALSE; + } + if (!itr->opener) + { + eina_strbuf_append(buf, "/"); + } + eina_strbuf_append(buf, itr->format); + } + } + evas_textblock_cursor_free(cur); + ret = eina_strbuf_string_steal(buf); + eina_strbuf_free(buf); + + return ret; +} + /** * @} */ diff --git a/src/lib/evas/canvas/evas_textblock.eo b/src/lib/evas/canvas/evas_textblock.eo index a087400414..e61322a604 100644 --- a/src/lib/evas/canvas/evas_textblock.eo +++ b/src/lib/evas/canvas/evas_textblock.eo @@ -192,6 +192,43 @@ class Evas.Textblock (Evas.Object) b: Evas.Coord; } } + @property annotation { + [[Annotation format in a given range in the text + + The annotation $format begins at $start and usually closes at + $end (inclusive). + + @since 1.18 + ]] + + set { + [[Sets an annotation format in a specified range [$start, $end] + in the text. + + This will add both opening and closing formats for the given + $format. There is an option to given a negative value for $end, + in which case there will be no closing format, that is, the format + will be applied all the way to the end of the text until it will + be closed in a later call. + ]] + return: bool; + } + get { + [[Retrieves the annotation format instances that exist in the given + range [$start, $end]. The returned string may be a space-separated + list if multiple format instances were added in that range. + Note that formats that take effect in that range, but were not + added in it (i.e. they were added before) will not be included. + ]] + } + keys { + start: int; + end: int; + } + values { + format: char *; + } + } line_number_geometry_get @const { [[Get the geometry of a line number.]] return: bool; [[$true on success, $false otherwise.]] diff --git a/src/tests/evas/evas_test_textblock.c b/src/tests/evas/evas_test_textblock.c index 1473f99354..fa79718db0 100644 --- a/src/tests/evas/evas_test_textblock.c +++ b/src/tests/evas/evas_test_textblock.c @@ -3952,6 +3952,57 @@ START_TEST(evas_textblock_hyphenation) END_TEST; #endif +static void +_test_check_annotation(Evas_Object *tb, + int start, int end, const char *format) +{ + char *tmp = evas_object_textblock_annotation_get(tb, start, end); + ck_assert_msg(!strcmp(format, tmp), + "format at range [%d, %d] \"%s\" doesn't match given format \"%s\".", + start, end, tmp, format); + if (tmp) free(tmp); +} + +START_TEST(evas_textblock_annotation) +{ + START_TB_TEST(); + const char *buf = + "This text will check annotation." + "By \"annotating\" the text, we can apply formatting simply by" + " specifying a range (start, end) in the text, and the format we want" + " for it." + ; + evas_object_textblock_text_markup_set(tb, buf); + ck_assert(evas_object_textblock_annotation_set(tb, 0, 3, NULL)); + ck_assert(evas_object_textblock_annotation_set(tb, 0, 3, "")); + ck_assert(!evas_object_textblock_annotation_set(tb, 1, 0, "color=#fff")); + + _test_check_annotation(tb, 0, 10, ""); + + evas_object_textblock_annotation_set(tb, 0, 3, "font_weight=bold"); + _test_check_annotation(tb, 0, 3, "font_weight=bold"); + _test_check_annotation(tb, 1, 3, ""); + + evas_object_textblock_annotation_set(tb, 50, 60, "color=#0ff"); + _test_check_annotation(tb, 0, 49, "font_weight=bold /font_weight=bold ps"); + _test_check_annotation(tb, 0, 50, "font_weight=bold /font_weight=bold ps color=#0ff"); + _test_check_annotation(tb, 0, 51, "font_weight=bold /font_weight=bold ps color=#0ff"); + _test_check_annotation(tb, 0, 59, "font_weight=bold /font_weight=bold ps color=#0ff"); + _test_check_annotation(tb, 0, 60, "font_weight=bold /font_weight=bold ps color=#0ff"); + _test_check_annotation(tb, 0, 61, "font_weight=bold /font_weight=bold ps color=#0ff /color=#0ff"); + _test_check_annotation(tb, 40, 51, "color=#0ff"); + _test_check_annotation(tb, 40, 61, "color=#0ff /color=#0ff"); + + evas_object_textblock_annotation_set(tb, 65, -1, "font_size=18"); + _test_check_annotation(tb, 66, 1000, ""); + _test_check_annotation(tb, 65, 1000, "font_size=18"); + _test_check_annotation(tb, 64, 1000, "font_size=18"); + _test_check_annotation(tb, 0, 1000, "font_weight=bold /font_weight=bold ps color=#0ff /color=#0ff font_size=18"); + + END_TB_TEST(); +} +END_TEST; + void evas_test_textblock(TCase *tc) { tcase_add_test(tc, evas_textblock_simple); @@ -3974,6 +4025,7 @@ void evas_test_textblock(TCase *tc) tcase_add_test(tc, evas_textblock_items); tcase_add_test(tc, evas_textblock_delete); tcase_add_test(tc, evas_textblock_obstacle); + tcase_add_test(tc, evas_textblock_annotation); #ifdef HAVE_HYPHEN tcase_add_test(tc, evas_textblock_hyphenation); #endif -- cgit v1.2.1