diff options
author | Daniel Hirt <daniel.hirt@samsung.com> | 2016-03-29 16:53:41 +0300 |
---|---|---|
committer | Daniel Hirt <daniel.hirt@samsung.com> | 2016-05-16 11:17:50 +0300 |
commit | b04295e5b56c2f3340380c759ce6baf593ac42a2 (patch) | |
tree | 61f8cf51d635e6f419572538efd475b2d86007ce | |
parent | 6a237f3cca3c62b1c05035e532a7abbc790b7cbb (diff) | |
download | efl-b04295e5b56c2f3340380c759ce6baf593ac42a2.tar.gz |
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,
"<color=#0ff>hel</color=#0ff>lo"
" w<font_weight=Bold>or</font_weight=Bold>ld");
@feature
-rw-r--r-- | src/lib/evas/Evas_Common.h | 10 | ||||
-rw-r--r-- | src/lib/evas/canvas/evas_object_textblock.c | 344 | ||||
-rw-r--r-- | src/lib/evas/canvas/evas_textblock.eo | 69 | ||||
-rw-r--r-- | src/tests/evas/evas_test_textblock.c | 121 |
4 files changed, 542 insertions, 2 deletions
diff --git a/src/lib/evas/Evas_Common.h b/src/lib/evas/Evas_Common.h index d44644b92f..df63c842d9 100644 --- a/src/lib/evas/Evas_Common.h +++ b/src/lib/evas/Evas_Common.h @@ -3070,6 +3070,16 @@ typedef struct _Evas_Textblock_Style Evas_Textblock_Style; typedef struct _Evas_Textblock_Cursor Evas_Textblock_Cursor; /** + * @typedef Evas_Textblock_Annotation + * + * A textblock annotation handle. This handle is used to to maipulate + * annotation instances in the textblock object. + * @see evas_object_textblock_annotation_insert + * + */ +typedef struct _Evas_Textblock_Annotation Evas_Textblock_Annotation; + +/** * @typedef Evas_Object_Textblock_Node_Format * A format node. * diff --git a/src/lib/evas/canvas/evas_object_textblock.c b/src/lib/evas/canvas/evas_object_textblock.c index 82bf141494..c31ef0129a 100644 --- a/src/lib/evas/canvas/evas_object_textblock.c +++ b/src/lib/evas/canvas/evas_object_textblock.c @@ -207,6 +207,12 @@ typedef struct _Evas_Object_Textblock_Format Evas_Object_Textblock_Format; typedef struct _Evas_Textblock_Selection_Iterator Evas_Textblock_Selection_Iterator; /** * @internal + * @typedef Evas_Textblock_Annotation_Iterator + * A textblock annotation iterator. + */ +typedef struct _Evas_Textblock_Annotation_Iterator Evas_Textblock_Annotation_Iterator; +/** + * @internal * @def IS_AT_END(ti, ind) * Return true if ind is at the end of the text item, false otherwise. */ @@ -299,6 +305,7 @@ struct _Evas_Textblock_Node_Format const char *orig_format; /**< Original format information. */ Evas_Object_Textblock_Node_Text *text_node; /**< The text node it's pointing to. */ size_t offset; /**< Offset from the last format node of the same text. */ + Evas_Textblock_Annotation *annotation; /**< Pointer to annotation handle. */ struct { unsigned char l, r, t, b; } pad; /**< Amount of padding required. */ @@ -479,6 +486,13 @@ struct _Evas_Textblock_Cursor Evas_Object_Textblock_Node_Text *node; }; +struct _Evas_Textblock_Annotation +{ + EINA_INLIST; + Evas_Object *obj; + Evas_Object_Textblock_Node_Format *start_node, *end_node; +}; + /* Size of the index array */ #define TEXTBLOCK_PAR_INDEX_SIZE 10 struct _Evas_Object_Textblock @@ -500,6 +514,7 @@ struct _Evas_Object_Textblock Eina_List *anchors_item; Eina_List *obstacles; Eina_List *hyphen_items; /* Hyphen items storage to free when clearing lines */ + Evas_Textblock_Annotation *annotations; /* Hyphen items storage to free when clearing lines */ int last_w, last_h; struct { int l, r, t, b; @@ -533,6 +548,13 @@ struct _Evas_Textblock_Selection_Iterator Eina_List *current; /**< Current node in loop. */ }; +struct _Evas_Textblock_Annotation_Iterator +{ + Eina_Iterator iterator; /**< Eina Iterator. */ + Eina_List *list; /**< Head of list. */ + Eina_List *current; /**< Current node in loop. */ +}; + /* private methods for textblock objects */ static void evas_object_textblock_init(Evas_Object *eo_obj); static void evas_object_textblock_render(Evas_Object *eo_obj, @@ -613,6 +635,8 @@ static size_t _evas_textblock_node_format_pos_get(const Evas_Object_Textblock_No static void _evas_textblock_node_format_remove(Evas_Textblock_Data *o, Evas_Object_Textblock_Node_Format *n, int visual_adjustment); static void _evas_textblock_node_format_free(Evas_Textblock_Data *o, Evas_Object_Textblock_Node_Format *n); static void _evas_textblock_node_text_free(Evas_Object_Textblock_Node_Text *n); +static void _evas_textblock_annotation_remove(Evas_Textblock_Data *o, Evas_Textblock_Annotation *an, Eina_Bool remove_nodes); +static void _evas_textblock_annotations_clear(Evas_Textblock_Data *o); static void _evas_textblock_changed(Evas_Textblock_Data *o, Evas_Object *eo_obj); static void _evas_textblock_invalidate_all(Evas_Textblock_Data *o); static void _evas_textblock_cursors_update_offset(const Evas_Textblock_Cursor *cur, const Evas_Object_Textblock_Node_Text *n, size_t start, int offset); @@ -800,6 +824,10 @@ static void _nodes_clear(const Evas_Object *eo_obj) { Evas_Textblock_Data *o = eo_data_scope_get(eo_obj, MY_CLASS); + + /* First, clear all annotations that may have spawned format nodes. */ + _evas_textblock_annotations_clear(o); + while (o->text_nodes) { Evas_Object_Textblock_Node_Text *n; @@ -8641,10 +8669,20 @@ _evas_textblock_node_format_remove_matching(Evas_Textblock_Data *o, if (_FORMAT_IS_CLOSER_OF( fnode->orig_format, fstr + 1, fstr_len - 1)) { + Eina_Bool have_annotation = !!fmt->annotation; + fnode = eina_list_data_get(i); formats = eina_list_remove_list(formats, i); _evas_textblock_node_format_remove(o, fnode, 0); _evas_textblock_node_format_remove(o, fmt, 0); + + /* Only matching format nodes may be the result + * of an annotation. */ + if (have_annotation) + { + _evas_textblock_annotation_remove( + o, fmt->annotation, EINA_FALSE); + } break; } } @@ -9543,8 +9581,9 @@ _evas_textblock_cursor_is_at_the_end(const Evas_Textblock_Cursor *cur) EINA_TRUE : EINA_FALSE; } -EAPI Eina_Bool -evas_textblock_cursor_format_append(Evas_Textblock_Cursor *cur, const char *format) +Eina_Bool +_evas_textblock_cursor_format_append(Evas_Textblock_Cursor *cur, + const char *format, Evas_Object_Textblock_Node_Format **_fnode) { Evas_Object_Textblock_Node_Format *n; Eina_Bool is_visible; @@ -9675,10 +9714,18 @@ evas_textblock_cursor_format_append(Evas_Textblock_Cursor *cur, const char *form if (!o->cursor->node) o->cursor->node = o->text_nodes; + + if (_fnode) *_fnode = n; return is_visible; } EAPI Eina_Bool +evas_textblock_cursor_format_append(Evas_Textblock_Cursor *cur, const char *format) +{ + return _evas_textblock_cursor_format_append(cur, format, NULL); +} + +EAPI Eina_Bool evas_textblock_cursor_format_prepend(Evas_Textblock_Cursor *cur, const char *format) { Eina_Bool is_visible; @@ -12847,6 +12894,299 @@ _evas_textblock_evas_object_paragraph_direction_get(Eo *eo_obj EINA_UNUSED, return o->paragraph_direction; } +/** annotation iterator */ +/** + * @internal + * Returns the value of the current data of list node, + * and goes to the next list node. + * + * @param it the iterator. + * @param data the data of the current list node. + * @return EINA_FALSE if unsuccessful. Otherwise, returns EINA_TRUE. + */ +static Eina_Bool +_evas_textblock_annotation_iterator_next(Evas_Textblock_Annotation_Iterator *it, void **data) +{ + if (!it->current) + return EINA_FALSE; + + *data = eina_list_data_get(it->current); + it->current = eina_list_next(it->current); + + return EINA_TRUE; +} + +/** + * @internal + * Frees the annotation iterator. + * @param it the iterator to free + * @return EINA_FALSE if unsuccessful. Otherwise, returns EINA_TRUE. + */ +static Eina_Bool +_evas_textblock_annotation_iterator_free(Evas_Textblock_Annotation_Iterator *it) +{ + EINA_MAGIC_SET(&it->iterator, 0); + it->current = NULL; + eina_list_free(it->list); + free(it); + return EINA_TRUE; +} + +/** + * @internal + * Creates newly allocated iterator associated to a list. + * @param list The list. + * @return If the memory cannot be allocated, NULL is returned. + * Otherwise, a valid iterator is returned. + */ +Eina_Iterator * +_evas_textblock_annotation_iterator_new(Eina_List *list) +{ + Evas_Textblock_Selection_Iterator *it; + + it = calloc(1, sizeof(Evas_Textblock_Annotation_Iterator)); + if (!it) return NULL; + + EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR); + it->list = list; + it->current = list; + + it->iterator.version = EINA_ITERATOR_VERSION; + it->iterator.next = FUNC_ITERATOR_NEXT( + _evas_textblock_annotation_iterator_next); + it->iterator.free = FUNC_ITERATOR_FREE( + _evas_textblock_annotation_iterator_free); + + return &it->iterator; +} + +static size_t +_node_text_position_get(Evas_Textblock_Data *o, Evas_Object_Textblock_Node_Text *node) +{ + Evas_Object_Textblock_Node_Text *n; + size_t ret = 0; + EINA_INLIST_FOREACH(o->text_nodes, n) + { + if (n == node) break; + ret += eina_ustrbuf_length_get(n->unicode); + } + if (!n) return 0; + return ret; +} + +static size_t +_textblock_fnode_pos_get(Evas_Textblock_Data *o, Evas_Object_Textblock_Node_Format *fnode) +{ + /* Get the relative offset to cur's text node */ + size_t off = _evas_textblock_node_format_pos_get(fnode); + size_t toff = _node_text_position_get(o, fnode->text_node); + + return off + toff; +} + +/* Annotation API - WIP */ + +static Eina_Bool +_textblock_annotation_set(Eo *eo_obj, Evas_Textblock_Data *o, + Evas_Textblock_Annotation *an, size_t start, size_t end, + const char *format) +{ + int len; + char *buf; + Evas_Textblock_Node_Format *fnode; + Evas_Textblock_Cursor *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, &fnode); + free(buf); + an->start_node = fnode; + fnode->annotation = an; + + /* Add closing format at end + 1 */ + evas_textblock_cursor_pos_set(cur, end + 1); + len = strlen(format); + buf = malloc(len + 4); + sprintf(buf, "</%s>", format); + _evas_textblock_cursor_format_append(cur, buf, &fnode); + free(buf); + an->end_node = fnode; + fnode->annotation = an; + evas_textblock_cursor_free(cur); + + o->format_changed = EINA_TRUE; + return EINA_TRUE; +} + +EOLIAN static const char * +_evas_textblock_annotation_get(Eo *eo_obj EINA_UNUSED, Evas_Textblock_Data *o EINA_UNUSED, + Evas_Textblock_Annotation *annotation) +{ + if (!annotation || (annotation->obj != eo_obj)) + { + ERR("Used invalid handle or of a different object"); + return EINA_FALSE; + } + + return (annotation->start_node ? annotation->start_node->format : NULL); +} + +EOLIAN static Eina_Bool +_evas_textblock_annotation_set(Eo *eo_obj EINA_UNUSED, + Evas_Textblock_Data *o, Evas_Textblock_Annotation *annotation, + const char *format) +{ + if (!annotation || (annotation->obj != eo_obj)) + { + ERR("Used invalid handle or of a different object"); + return EINA_FALSE; + } + + if (!annotation->start_node || !annotation->end_node) return EINA_FALSE; + if (!format || (format[0] == '\0')) return EINA_FALSE; + + /* XXX: Not efficient but works and saves code */ + size_t start = _textblock_fnode_pos_get(o, annotation->start_node); + size_t end = _textblock_fnode_pos_get(o, annotation->end_node) - 1; + + _evas_textblock_node_format_remove(o, annotation->start_node, 0); + _evas_textblock_node_format_remove(o, annotation->end_node, 0); + + _textblock_annotation_set(eo_obj, o, annotation, start, end, format); + + return EINA_TRUE; +} + +static void +_evas_textblock_annotation_remove(Evas_Textblock_Data *o, + Evas_Textblock_Annotation *an, Eina_Bool remove_nodes) +{ + if (remove_nodes) + { + _evas_textblock_node_format_remove(o, an->start_node, 0); + _evas_textblock_node_format_remove(o, an->end_node, 0); + } + o->annotations = (Evas_Textblock_Annotation *) + eina_inlist_remove(EINA_INLIST_GET(o->annotations), + EINA_INLIST_GET(an)); + free(an); +} + +static void +_evas_textblock_annotations_clear(Evas_Textblock_Data *o) +{ + Evas_Textblock_Annotation *an; + + EINA_INLIST_FREE(o->annotations, an) + { + _evas_textblock_annotation_remove(o, an, EINA_TRUE); + } +} + +EOLIAN static Eina_Bool +_evas_textblock_annotation_del(Eo *eo_obj EINA_UNUSED, + Evas_Textblock_Data *o, Evas_Textblock_Annotation *annotation) +{ + if (!annotation || (annotation->obj != eo_obj)) + { + ERR("Used invalid handle or of a different object"); + return EINA_FALSE; + } + + _evas_textblock_annotation_remove(o, annotation, EINA_TRUE); + o->format_changed = EINA_TRUE; + + //XXX: It's a workaround. The underlying problem is that only new format + // nodes are checks when their respective text nodes are invalidated (see + // _format_changes_invalidate_text_nodes). Complete removal of the format + // nodes was not handled properly (as formats could only be removed via + // text changes e.g. deleting characters). + _evas_textblock_invalidate_all(o); + + _evas_textblock_changed(o, eo_obj); + return EINA_TRUE; +} + +EOLIAN static Evas_Textblock_Annotation * +_evas_textblock_annotation_insert(Eo *eo_obj, Evas_Textblock_Data *o, + size_t start, size_t end EINA_UNUSED, const char *format) +{ + Evas_Textblock_Annotation *ret = NULL; + Eina_Strbuf *buf; + Eina_Bool first = EINA_TRUE; + const char *item; + + if (!format || (format[0] == '\0') || (end <= start)) return NULL; + + /* Sanitize the string and reject format items, closing '/' marks. */ + buf = eina_strbuf_new(); + while ((item = _format_parse(&format))) + { + int itlen = format - item; + /* We care about all of the formats even after a - except for + * item which we don't care after a - because it's just a standard + * closing */ + if ((!strncmp(item, "\n", itlen) || !strncmp(item, "\\n", itlen)) || + (!strncmp(item, "\t", itlen) || !strncmp(item, "\\t", itlen)) || + (!strncmp(item, "br", itlen) && (itlen >= 2)) || + (!strncmp(item, "tab", itlen) && (itlen >= 3)) || + (!strncmp(item, "ps", itlen) && (itlen >= 2)) || + (!strncmp(item, "item", itlen) && (itlen >= 4))) + { + continue; + } + if (first) + { + first = EINA_FALSE; + } + else + { + eina_strbuf_append_length(buf, " ", 1); + } + eina_strbuf_append_length(buf, item, itlen); + } + + format = eina_strbuf_string_steal(buf); + if (!format || (format[0] == '\0')) return NULL; + + ret = calloc(1, sizeof(Evas_Textblock_Annotation)); + ret->obj = eo_obj; + + o->annotations = (Evas_Textblock_Annotation *) + eina_inlist_append(EINA_INLIST_GET(o->annotations), + EINA_INLIST_GET(ret)); + + _textblock_annotation_set(eo_obj, o, ret, start, end, format); + + _evas_textblock_changed(o, eo_obj); + + return ret; +} + +EOLIAN static Eina_Iterator * +_evas_textblock_annotation_range_get_all(Eo *eo_obj EINA_UNUSED, Evas_Textblock_Data *o EINA_UNUSED, + size_t start, size_t end) +{ + Eina_List *lst = NULL; + Evas_Textblock_Annotation *it; + + EINA_INLIST_FOREACH(o->annotations, it) + { + size_t start1, end1; + if (!it->start_node || !it->end_node) continue; + start1 = _textblock_fnode_pos_get(o, it->start_node); + end1 = _textblock_fnode_pos_get(o, it->end_node) - 1; + if (!((start1 > end) || (end1 < start))) + { + lst = eina_list_append(lst, it); + } + } + return _evas_textblock_annotation_iterator_new(lst); +} + /** * @} */ diff --git a/src/lib/evas/canvas/evas_textblock.eo b/src/lib/evas/canvas/evas_textblock.eo index a087400414..1d8fa6b6b9 100644 --- a/src/lib/evas/canvas/evas_textblock.eo +++ b/src/lib/evas/canvas/evas_textblock.eo @@ -1,5 +1,6 @@ struct @extern Evas.Textblock.Cursor; struct @extern Evas.Textblock.Style; +struct @extern Evas.Textblock.Annotation; struct Evas.Textblock.Node_Format; class Evas.Textblock (Evas.Object) @@ -192,6 +193,30 @@ class Evas.Textblock (Evas.Object) b: Evas.Coord; } } + @property annotation { + set { + [[Sets a new format for $annotation. + + This will replace the format applied by $annotation with $format. + Assumes that $annotation is a handle for an existing annotation, + i.e. one that was added using @.annotation_insert to this object. + Otherwise, this will fail and return $false. + ]] + return: bool; [[$true on success, $false otherwise.]] + } + get { + [[Returns the format (string) of this annotation. + + @since 1.18 + ]] + } + keys { + annotation: Evas.Textblock.Annotation *; + } + values { + format: const(char)*; + } + } line_number_geometry_get @const { [[Get the geometry of a line number.]] return: bool; [[$true on success, $false otherwise.]] @@ -320,6 +345,50 @@ class Evas.Textblock (Evas.Object) @since 1.15 ]] } + annotation_insert { + [[Inserts an annotation format in a specified range [$start, $end] + in the text. + + This will add both opening and closing formats for the given + $format. + Returns a handle to manipulate the inserted annotation. + + @since 1.18 + ]] + /* + return: free(own(iterator<Evas.Textblock.Annotation *> *), + eina_iterator_free); [[Handle of the Annotation]] + */ + return: Evas.Textblock.Annotation *; + params { + @in start: size; + @in end: size; + @in format: const(char)*; + } + } + annotation_range_get_all { + [[Returns an iterator of all the handles in a range. + + @since 1.18 + ]] + return: free(own(iterator<Evas.Textblock.Annotation *> *), + eina_iterator_free); [[Handle of the Annotation]] + params { + @in start: size; + @in end: size; + } + } + annotation_del { + [[Deletes $annotation. + + All formats applied by $annotations will be removed and it will be + deleted. + ]] + return: bool; [[$true on success, $false otherwise.]] + params { + @in annotation: Evas.Textblock.Annotation *; + } + } } implements { Eo.Base.constructor; diff --git a/src/tests/evas/evas_test_textblock.c b/src/tests/evas/evas_test_textblock.c index 9120b228da..814c96251f 100644 --- a/src/tests/evas/evas_test_textblock.c +++ b/src/tests/evas/evas_test_textblock.c @@ -3973,6 +3973,126 @@ START_TEST(evas_textblock_hyphenation) END_TEST; #endif +static void +_test_check_annotation(Evas_Object *tb, + int start, int end, size_t len, const char **formats) +{ + Evas_Textblock_Annotation *an; + Eina_Iterator *it = + evas_object_textblock_annotation_range_get_all(tb, start, end); + size_t i = 0; + EINA_ITERATOR_FOREACH(it, an) + { + const char *fmt = evas_object_textblock_annotation_get(tb, + an); + ck_assert_msg((i < len), + "No formats to check but current annotation is: %s\n", fmt); + ck_assert_str_eq(fmt, *formats); + formats++; + i++; + } + ck_assert_msg((i == len), + "Expected next format (index %lu): %s, but reached end of annotations\n", + i, *formats); + + eina_iterator_free(it); +} + +#define _COMP_STR(...) ((const char *[]) { __VA_ARGS__ }) +#define _CREATE_PARAMS(X) (sizeof(X) / sizeof(X[0])), (X) +#define _COMP_PARAMS(...) _CREATE_PARAMS(_COMP_STR(__VA_ARGS__)) + +START_TEST(evas_textblock_annotation) +{ + START_TB_TEST(); + Evas_Textblock_Annotation *an, *an2; + 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); + + /* Check some trivial cases */ + ck_assert(!evas_object_textblock_annotation_insert(tb, 0, 3, NULL)); + ck_assert(!evas_object_textblock_annotation_insert(tb, 0, 3, "")); + ck_assert(!evas_object_textblock_annotation_insert(tb, 1, 0, "color=#fff")); + + /* Insert and check correct positions */ + _test_check_annotation(tb, 0, 10, _COMP_PARAMS()); + + evas_object_textblock_annotation_insert(tb, 0, 3, "font_weight=bold"); + _test_check_annotation(tb, 0, 3, _COMP_PARAMS("font_weight=bold")); + _test_check_annotation(tb, 0, 3, _COMP_PARAMS("font_weight=bold")); + _test_check_annotation(tb, 4, 10, _COMP_PARAMS()); + + evas_object_textblock_annotation_insert(tb, 50, 60, "color=#0ff"); + _test_check_annotation(tb, 0, 49, _COMP_PARAMS("font_weight=bold")); + _test_check_annotation(tb, 0, 51, _COMP_PARAMS("font_weight=bold", "color=#0ff")); + _test_check_annotation(tb, 0, 59, _COMP_PARAMS("font_weight=bold", "color=#0ff")); + _test_check_annotation(tb, 0, 60, _COMP_PARAMS("font_weight=bold", "color=#0ff")); + _test_check_annotation(tb, 40, 50, _COMP_PARAMS("color=#0ff")); + _test_check_annotation(tb, 40, 51, _COMP_PARAMS("color=#0ff")); + _test_check_annotation(tb, 40, 61, _COMP_PARAMS("color=#0ff")); + _test_check_annotation(tb, 60, 61, _COMP_PARAMS("color=#0ff")); + _test_check_annotation(tb, 61, 62, _COMP_PARAMS()); + + /* See that annotation's positions are updated as text is inserted */ + evas_object_textblock_text_markup_set(tb, "hello"); + an = evas_object_textblock_annotation_insert(tb, 0, 2, "color=#fff"); + _test_check_annotation(tb, 3, 3, _COMP_PARAMS()); + evas_textblock_cursor_pos_set(cur, 0); + evas_textblock_cursor_text_append(cur, "a"); + _test_check_annotation(tb, 3, 3, _COMP_PARAMS("color=#fff")); + _test_check_annotation(tb, 4, 4, _COMP_PARAMS()); + + /* Replace annotations's format */ + evas_object_textblock_annotation_set(tb, an, "font_size=14"); + _test_check_annotation(tb, 3, 3, _COMP_PARAMS("font_size=14")); + _test_check_annotation(tb, 4, 4, _COMP_PARAMS()); + + evas_object_textblock_text_markup_set(tb, "hello world"); + an = evas_object_textblock_annotation_insert(tb, 0, 1, "color=#fff"); + an2 = evas_object_textblock_annotation_insert(tb, 2, 3, "font_size=14"); + _test_check_annotation(tb, 0, 1, _COMP_PARAMS("color=#fff")); + _test_check_annotation(tb, 2, 3, _COMP_PARAMS("font_size=14")); + _test_check_annotation(tb, 0, 3, _COMP_PARAMS("color=#fff", "font_size=14")); + evas_object_textblock_annotation_set(tb, an, "font_size=10"); + evas_object_textblock_annotation_set(tb, an2, "color=#000"); + _test_check_annotation(tb, 2, 3, _COMP_PARAMS("color=#000")); + _test_check_annotation(tb, 0, 1, _COMP_PARAMS("font_size=10")); + _test_check_annotation(tb, 0, 3, _COMP_PARAMS("font_size=10", "color=#000")); + + /* Delete annotations directly */ + evas_object_textblock_text_markup_set(tb, "hello world"); + an = evas_object_textblock_annotation_insert(tb, 0, 1, "color=#fff"); + an2 = evas_object_textblock_annotation_insert(tb, 2, 3, "font_size=14"); + evas_object_textblock_annotation_del(tb, an); + _test_check_annotation(tb, 0, 3, _COMP_PARAMS("font_size=14")); + evas_object_textblock_annotation_del(tb, an2); + _test_check_annotation(tb, 0, 3, _COMP_PARAMS()); + an = evas_object_textblock_annotation_insert(tb, 0, 1, "color=#fff"); + _test_check_annotation(tb, 2, 3, _COMP_PARAMS()); + _test_check_annotation(tb, 1, 1, _COMP_PARAMS("color=#fff")); + evas_object_textblock_annotation_del(tb, an); + _test_check_annotation(tb, 1, 1, _COMP_PARAMS()); + + /* Check blocking of "item formats" */ + evas_object_textblock_text_markup_set(tb, "hello world"); + an = evas_object_textblock_annotation_insert(tb, 0, 1, "ps"); + _test_check_annotation(tb, 0, 1, _COMP_PARAMS()); + an = evas_object_textblock_annotation_insert(tb, 0, 1, "color=#fff"); + _test_check_annotation(tb, 0, 1, _COMP_PARAMS("color=#fff")); + an = evas_object_textblock_annotation_insert(tb, 2, 3, "br"); + an = evas_object_textblock_annotation_insert(tb, 6, 7, "item"); + _test_check_annotation(tb, 0, 8, _COMP_PARAMS("color=#fff")); + + END_TB_TEST(); +} +END_TEST; + void evas_test_textblock(TCase *tc) { tcase_add_test(tc, evas_textblock_simple); @@ -3995,6 +4115,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 |