summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Hirt <daniel.hirt@samsung.com>2016-03-29 16:53:41 +0300
committerDaniel Hirt <daniel.hirt@samsung.com>2016-05-16 11:17:50 +0300
commitb04295e5b56c2f3340380c759ce6baf593ac42a2 (patch)
tree61f8cf51d635e6f419572538efd475b2d86007ce
parent6a237f3cca3c62b1c05035e532a7abbc790b7cbb (diff)
downloadefl-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.h10
-rw-r--r--src/lib/evas/canvas/evas_object_textblock.c344
-rw-r--r--src/lib/evas/canvas/evas_textblock.eo69
-rw-r--r--src/tests/evas/evas_test_textblock.c121
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