summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Hirt <daniel.hirt@samsung.com>2014-05-25 11:14:42 +0300
committerDaniel Hirt <daniel.hirt@samsung.com>2014-05-29 17:57:26 +0300
commit233dae393e4ce03e8f096d37f8e037e6745f4dc7 (patch)
tree02303ac3e3267fdde489eef6d7321c3ce1b439ca
parentdc3178404f2d82cddf5be19de03dd81cdeab7541 (diff)
downloadefl-devs/herdsman/tb_ellipsis.tar.gz
Evas/Textblock: add support for ellipsis valuesdevs/herdsman/tb_ellipsis
This enables textblock to support more values other than 1.0. For 0 <= ellipsis < 1.0, it splits the text such that it fits the textblock's width. The ellipsis is relatively position according to the ellipsis value, and characters are removed also relatively. For example, a value of 0.5 will position the ellipsis right in the center of the textblock's width, while removing characters equally right and left from the center. Basic approach to this feature was to do some work before the layout process. We calculate the expected total width of the items, and by how much we exceed from the textblock's width. Afterwards is it just some careful work to set the boundaries of the width we want to cut, and deciding which characters we need to removed to fulfill this requirement. The rest is splitting the text and visually-removing the part we need to cut. This is all handled before any logical lines are created, so the _layout_par function remains almost intact. A designated _ellip_prev_it field in the Paragraph struct instructs after which item we place the ellipsis item, if at all. Note that we keep the fast path for ellipsis 1.0, as heavier work needs to be done for the other values. Also, added tests to evas_suite for a range of ellipsis values. This is a first draft that is initended for review purposes only. Also, multiline is unsupported at the moment. @feature
-rw-r--r--src/lib/evas/canvas/evas_object_textblock.c162
-rw-r--r--src/tests/evas/evas_test_textblock.c51
2 files changed, 204 insertions, 9 deletions
diff --git a/src/lib/evas/canvas/evas_object_textblock.c b/src/lib/evas/canvas/evas_object_textblock.c
index 427a1ad763..c6d9770db5 100644
--- a/src/lib/evas/canvas/evas_object_textblock.c
+++ b/src/lib/evas/canvas/evas_object_textblock.c
@@ -487,6 +487,7 @@ struct _Evas_Object_Textblock
Evas_Object_Textblock_Paragraph *par_index[TEXTBLOCK_PAR_INDEX_SIZE];
Evas_Object_Textblock_Text_Item *ellip_ti;
+ Eina_List *ellip_prev_it; /* item that is placed before ellipsis item (0.0 <= ellipsis < 1.0), if required */
Eina_List *anchors_a;
Eina_List *anchors_item;
int last_w, last_h;
@@ -2910,6 +2911,7 @@ _layout_format_push(Ctxt *c, Evas_Object_Textblock_Format *fmt,
fmt->underline_dash_gap = 2;
fmt->linerelgap = 0.0;
fmt->password = 1;
+ fmt->ellipsis = -1;
}
return fmt;
}
@@ -4471,6 +4473,142 @@ _layout_paragraph_render(Evas_Textblock_Data *o,
#endif
}
+/* calculates items width in current paragraph */
+static inline Evas_Coord
+_calc_items_width(Ctxt *c)
+{
+ Evas_Object_Textblock_Item *it, *last_it = NULL;
+ Eina_List *i;
+ Evas_Coord w = 0;
+
+ if (!c->par->logical_items)
+ return 0;
+
+ EINA_LIST_FOREACH(c->par->logical_items, i, it)
+ {
+ w += it->adv;
+ last_it = it;
+ }
+
+ //reaching this point when it is the last item
+ if (last_it)
+ w += last_it->w - last_it->adv;
+ return w;
+}
+
+static inline int
+_item_get_cutoff(Ctxt *c, Evas_Object_Textblock_Item *it, Evas_Coord x)
+{
+ int pos = -1;
+ Evas_Object_Textblock_Text_Item *ti;
+ Evas_Object_Protected_Data *obj = eo_data_scope_get(c->obj, EVAS_OBJ_CLASS);
+
+ ti = (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ? _ITEM_TEXT(it) : NULL;
+ if (ti && ti->parent.format->font.font)
+ {
+ pos = ENFN->font_last_up_to_pos(ENDT, ti->parent.format->font.font,
+ &ti->text_props, x, 0);
+ }
+ return pos;
+}
+
+/**
+ * @internal
+ * This handles ellipsis prior most of the work in _layout_par.
+ * Currently it is here to handle all value in the range of 0.0 to 0.9999 (<1).
+ * It starts by getting the total width of items, and calculates the 'block' of
+ * text that needs to be removed i.e. sets low and high boundaries
+ * of that block.
+ * All text items that intersect this block will be cut: the edge items (ones
+ * that don't intersect in whole) will be split, and the rest are set to be
+ * visually-deleted.
+ * Note that a special case for visible format items does not
+ * split them, but instead just visually-deletes them (because there are no
+ * characters to split).
+ */
+static inline void
+_layout_par_ellipsis_items(Ctxt *c, double ellip)
+{
+ Evas_Object_Textblock_Item *it;
+ Evas_Object_Textblock_Text_Item *ellip_ti;
+ Eina_List *i, *j;
+ Evas_Coord items_width, exceed, items_cut;
+ Evas_Coord l, h, off;
+ int pos;
+
+ c->o->ellip_prev_it = NULL;
+
+ /* calc exceed amount */
+ items_width = _calc_items_width(c);
+ exceed = items_width - (c->w - c->o->style_pad.l - c->o->style_pad.r
+ - c->marginl - c->marginr);
+
+ if (exceed <= 0)
+ return;
+
+ {
+ Evas_Object_Textblock_Item *first_it =
+ _ITEM(eina_list_data_get(c->par->logical_items));
+ ellip_ti = _layout_ellipsis_item_new(c, first_it);
+ }
+ exceed += ellip_ti->parent.adv;
+ items_cut = items_width * ellip;
+ l = items_cut - (exceed * ellip);
+ h = l + exceed; //h = items_cut - (exceed * (1 - ellip))
+
+ off = 0;
+ /* look for the item that is being cut by the lower boundary */
+ i = c->par->logical_items;
+ EINA_LIST_FOREACH(c->par->logical_items, i, it)
+ {
+ if (it->w > (l - off))
+ break;
+ off += it->adv;
+ }
+ c->o->ellip_prev_it = i;
+ if (it) _layout_ellipsis_item_new(c, it);
+
+
+ pos = (it && it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
+ (_item_get_cutoff(c, it, l - off)) : -1;
+ if (pos >= 0)
+ {
+ _layout_item_text_split_strip_white(c, _ITEM_TEXT(it), i, pos);
+ off += it->adv;
+ i = eina_list_next(i);
+ }
+
+ /* look for the item that is being cut by the upper boundary */
+ EINA_LIST_FOREACH(i, j, it)
+ {
+ if (it->w > (h - off))
+ break;
+ off += it->adv;
+ /* if item is not being cut by the upper boundary, then
+ * it is contained in the area that we are supposed to
+ * visually remove */
+ it->visually_deleted = EINA_TRUE;
+ }
+
+ pos = (it && it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
+ (_item_get_cutoff(c, it, h - off)) : -1;
+ if (pos >= 0)
+ _layout_item_text_split_strip_white(c, _ITEM_TEXT(it), j, pos + 1);
+ if (it)
+ it->visually_deleted = EINA_TRUE;
+}
+
+static inline void
+_layout_par_append_ellipsis(Ctxt *c)
+{
+ Evas_Object_Textblock_Text_Item *ellip_ti = c->o->ellip_ti;
+ c->ln->items = (Evas_Object_Textblock_Item *)
+ eina_inlist_append(EINA_INLIST_GET(c->ln->items),
+ EINA_INLIST_GET(_ITEM(ellip_ti)));
+ ellip_ti->parent.ln = c->ln;
+ c->x += ellip_ti->parent.adv;
+}
+
/* 0 means go ahead, 1 means break without an error, 2 means
* break with an error, should probably clean this a bit (enum/macro)
* FIXME ^ */
@@ -4550,6 +4688,18 @@ _layout_par(Ctxt *c)
_layout_line_new(c, it->format);
/* We walk on our own because we want to be able to add items from
* inside the list and then walk them on the next iteration. */
+
+ /* TODO: We need to consider where ellipsis is used in the current text.
+ Currently, we assume that ellipsis is at the beginning of the
+ paragraph. This is a safe assumption for now, as other usages
+ seem a bit unnatural.*/
+ {
+ double ellip;
+ ellip = it->format->ellipsis;
+ if ((0 <= ellip) && (ellip < 1.0))
+ _layout_par_ellipsis_items(c, ellip);
+ }
+
for (i = c->par->logical_items ; i ; )
{
Evas_Coord prevdescent = 0, prevascent = 0;
@@ -4559,6 +4709,10 @@ _layout_par(Ctxt *c)
/* Skip visually deleted items */
if (it->visually_deleted)
{
+ //one more chance for ellipsis special cases
+ if (c->o->ellip_prev_it == i)
+ _layout_par_append_ellipsis(c);
+
i = eina_list_next(i);
continue;
}
@@ -4596,7 +4750,11 @@ _layout_par(Ctxt *c)
c->marginl - c->marginr)) || (wrap > 0)))
{
/* Handle ellipsis here. If we don't have more width left
- * and no height left, or no more width left and no wrapping. */
+ * and no height left, or no more width left and no wrapping.
+ * Note that this is only for ellipsis == 1.0, and is treated in a
+ * fast path.
+ * Other values of 0.0 <= ellipsis < 1.0 are handled in
+ * _layout_par_ellipsis_items */
if ((it->format->ellipsis == 1.0) && (c->h >= 0) &&
((2 * it->h + c->y >
c->h - c->o->style_pad.t - c->o->style_pad.b) ||
@@ -4766,6 +4924,8 @@ _layout_par(Ctxt *c)
}
}
c->x += it->adv;
+ if (c->o->ellip_prev_it == i)
+ _layout_par_append_ellipsis(c);
i = eina_list_next(i);
}
if (adv_line)
diff --git a/src/tests/evas/evas_test_textblock.c b/src/tests/evas/evas_test_textblock.c
index 8b23393305..4c48b60ae2 100644
--- a/src/tests/evas/evas_test_textblock.c
+++ b/src/tests/evas/evas_test_textblock.c
@@ -1733,19 +1733,54 @@ START_TEST(evas_textblock_wrapping)
/* Ellipsis */
- evas_object_textblock_text_markup_set(tb, "aaaaaaaaaa");
- evas_textblock_cursor_format_prepend(cur, "+ ellipsis=1.0");
- evas_object_textblock_size_native_get(tb, &nw, &nh);
- evas_object_resize(tb, nw / 2, nh);
- evas_object_textblock_size_formatted_get(tb, &w, &h);
- fail_if((w > (nw / 2)) || (h != nh));
-
evas_object_textblock_text_markup_set(tb, "aaaaaaaaaaaaaaaaaa<br/>b");
evas_textblock_cursor_format_prepend(cur, "+ ellipsis=1.0 wrap=word");
evas_object_textblock_size_native_get(tb, &nw, &nh);
evas_object_resize(tb, nw / 2, nh * 2);
evas_object_textblock_size_formatted_get(tb, &w, &h);
- fail_if(w > (nw / 2));
+ ck_assert_int_le(w, (nw / 2));
+
+ {
+ double ellip;
+ for(ellip = 0.0; ellip <= 1.0; ellip = ellip + 0.1)
+ {
+ char buf[128];
+ Evas_Coord w1, h1, w2, h2;
+
+ sprintf(buf, "+ ellipsis=%f", ellip);
+ evas_object_textblock_text_markup_set(tb, "aaaaaaaaaa");
+ evas_textblock_cursor_format_prepend(cur, buf);
+ evas_object_textblock_size_native_get(tb, &nw, &nh);
+ evas_object_resize(tb, nw / 2, nh);
+ evas_object_textblock_size_formatted_get(tb, &w, &h);
+ ck_assert_int_le(w, (nw / 2));
+ ck_assert_int_eq(h, nh);
+
+ evas_object_textblock_text_markup_set(tb, "aaaaaaaaaa");
+ evas_textblock_cursor_format_prepend(cur, buf);
+ evas_object_textblock_size_native_get(tb, &nw, &nh);
+ evas_object_resize(tb, nw, nh);
+ evas_object_textblock_size_formatted_get(tb, &w, &h);
+ evas_object_resize(tb, nw / 2, nh);
+ evas_object_textblock_size_formatted_get(tb, &w1, &h1);
+ evas_object_resize(tb, nw, nh);
+ evas_object_textblock_size_formatted_get(tb, &w2, &h2);
+ ck_assert_int_eq(w, w2);
+ ck_assert_int_eq(h, h2);
+
+ sprintf(buf, "+ ellipsis=%f", ellip);
+ evas_object_textblock_text_markup_set(tb,
+ "the<tab>quick brown fox"
+ "jumps<tab> over the<tab> lazy dog"
+ );
+ evas_textblock_cursor_format_prepend(cur, buf);
+ evas_object_textblock_size_native_get(tb, &nw, &nh);
+ evas_object_resize(tb, nw / 2, nh);
+ evas_object_textblock_size_formatted_get(tb, &w, &h);
+ ck_assert_int_le(w, (nw / 2));
+ ck_assert_int_eq(h, nh);
+ }
+ }
/* Word wrap ending with whites. */
evas_object_resize(tb, 322, 400);