From a16f6e2330c04b77aacc372bc7a13e064bc379eb Mon Sep 17 00:00:00 2001 From: Cedric BAIL Date: Wed, 12 Dec 2012 14:05:29 +0000 Subject: [PATCH] evas: add ellipsis support in evas. Patch done with help from Tom Hacohen. SVN revision: 80763 --- ChangeLog | 12 +- NEWS | 2 + src/lib/evas/Evas.h | 58 +++++ src/lib/evas/canvas/evas_object_text.c | 298 ++++++++++++++++++++++--- 4 files changed, 338 insertions(+), 32 deletions(-) diff --git a/ChangeLog b/ChangeLog index dc598693a2..c593a9053b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2012-12-12 Cedric Bail + + * Add ellipsis support in Evas_Object_Text. + +2012-12-12 ChunEon park + + * Fix the evas gl line incorrect position problem. + 2012-12-11 Cedric Bail * Fix leak eet_pbkdf2_sha1 with OpenSSL. @@ -152,7 +160,3 @@ * Fix EINA_INLIST_FOREACH_SAFE macro to work when inlist is not the first item in the struct. - -2012-12-12 ChunEon park - - * Fix the evas gl line incorrect position problem. diff --git a/NEWS b/NEWS index 4bb4437a65..d3122b07a0 100644 --- a/NEWS +++ b/NEWS @@ -39,6 +39,8 @@ Additions: * ecore_evas_buffer: - Add window profile support. * ecore_getopt: add ECORE_GETOPT_ACTION_BREAK + * evas: + - Add ellipsis support in Evas_Object_Text. Deprecations: * ecore_x: diff --git a/src/lib/evas/Evas.h b/src/lib/evas/Evas.h index 8c30a1dde6..03b698a51e 100644 --- a/src/lib/evas/Evas.h +++ b/src/lib/evas/Evas.h @@ -9583,6 +9583,8 @@ enum EVAS_OBJ_TEXT_SUB_ID_OUTLINE_COLOR_SET, EVAS_OBJ_TEXT_SUB_ID_OUTLINE_COLOR_GET, EVAS_OBJ_TEXT_SUB_ID_STYLE_PAD_GET, + EVAS_OBJ_TEXT_SUB_ID_ELLIPSIS_SET, + EVAS_OBJ_TEXT_SUB_ID_ELLIPSIS_GET, EVAS_OBJ_TEXT_SUB_ID_LAST }; @@ -9978,6 +9980,36 @@ enum */ #define evas_obj_text_style_pad_get(l, r, t, b) EVAS_OBJ_TEXT_ID(EVAS_OBJ_TEXT_SUB_ID_STYLE_PAD_GET), EO_TYPECHECK(int *, l), EO_TYPECHECK(int *, r), EO_TYPECHECK(int *, t), EO_TYPECHECK(int *, b) + +/** + * @def evas_obj_text_ellipsis_set + * @since 1.8 + * + * Sets the ellipsis to apply on the given text object. + * 0.0 -> means apply ellipsis on the right end of the text, + * 1.0 -> means apply ellipsis on the start left of the text. + * + * @param[in] ellipsis in + * + * @see evas_object_text_ellipsis_get + */ +#define evas_obj_text_ellipsis_set(ellipsis) EVAS_OBJ_TEXT_ID(EVAS_OBJ_TEXT_SUB_ID_ELLIPSIS_SET), EO_TYPECHECK(double, ellipsis) + +/** + * @def evas_obj_text_ellipsis_get + * @since 1.8 + * + * Sets the ellipsis to apply on the given text object. + * 0.0 -> means apply ellipsis on the right end of the text, + * 1.0 -> means apply ellipsis on the start left of the text. + * value below 0, means no ellipsis. + * + * @param[out] ellipsis out + * + * @see evas_object_text_ellipsis_set + */ +#define evas_obj_text_ellipsis_get(ellipsis) EVAS_OBJ_TEXT_ID(EVAS_OBJ_TEXT_SUB_ID_ELLIPSIS_GET), EO_TYPECHECK(double *, ellipsis) + /** * Creates a new text object on the provided canvas. * @@ -10102,6 +10134,32 @@ EAPI void evas_object_text_bidi_delimiters_set(Evas_Object *obj, */ EAPI const char *evas_object_text_bidi_delimiters_get(const Evas_Object *obj); +/** + * @brief Sets the ellipsis that should be used for the text object. + * + * This is a value between 0.0 and 1.0 indicating the position of the text + * to be shown. 0.0 means the start will be shown and the end trimmed, 1.0 + * means the beginning will be trimmed and the end will be shown, and any value + * in between will cause ellipsis to be added in both end of the text and the + * requested part to be shown. + * -1.0 means ellipsis is turned off. + * + * @param obj The given text object. + * @param ellipsis the ellipsis. + * @since 1.8 + */ +EAPI void evas_object_text_ellipsis_set(Evas_Object *obj, double ellipsis); + +/** + * @brief Gets the ellipsis currently set on the text object. + * + * @param obj The given text object. + * @return The ellipsis set on the text object. + * @see evas_object_text_ellipsis_set. + * @since 1.8 + */ +EAPI double evas_object_text_ellipsis_get(const Evas_Object *obj); + EAPI Evas_Coord evas_object_text_ascent_get(const Evas_Object *obj) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); EAPI Evas_Coord evas_object_text_descent_get(const Evas_Object *obj) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); EAPI Evas_Coord evas_object_text_max_ascent_get(const Evas_Object *obj) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1); diff --git a/src/lib/evas/canvas/evas_object_text.c b/src/lib/evas/canvas/evas_object_text.c index b69798e17f..f31cd769ba 100644 --- a/src/lib/evas/canvas/evas_object_text.c +++ b/src/lib/evas/canvas/evas_object_text.c @@ -33,6 +33,7 @@ struct _Evas_Object_Text } outline, shadow, glow, glow2; unsigned char style; + double ellipsis; } cur, prev; float ascent, descent; @@ -131,19 +132,22 @@ _evas_object_text_item_clean(Evas_Object_Text_Item *it) evas_common_text_props_content_unref(&it->text_props); } +static void +_evas_object_text_item_del(Evas_Object_Text *o, Evas_Object_Text_Item *it) +{ + o->items = (Evas_Object_Text_Item *) eina_inlist_remove( + EINA_INLIST_GET(o->items), + EINA_INLIST_GET(it)); + _evas_object_text_item_clean(it); + free(it); +} + static void _evas_object_text_items_clear(Evas_Object_Text *o) { - Evas_Object_Text_Item *it; - while (o->items) { - it = o->items; - o->items = (Evas_Object_Text_Item *) eina_inlist_remove( - EINA_INLIST_GET(o->items), - EINA_INLIST_GET(it)); - _evas_object_text_item_clean(it); - free(it); + _evas_object_text_item_del(o, o->items); } } @@ -453,6 +457,16 @@ _text_font_get(Eo *eo_obj EINA_UNUSED, void *_pd, va_list *list) if (size) *size = o->cur.size; } +static void +_evas_object_text_item_update_sizes(Evas_Object_Protected_Data *obj, Evas_Object_Text *o, Evas_Object_Text_Item *it) +{ + ENFN->font_string_size_get(ENDT, + o->font, + &it->text_props, + &it->w, &it->h); + it->adv = ENFN->font_h_advance_get(ENDT, o->font, + &it->text_props); +} /** * @internal @@ -463,11 +477,12 @@ _text_font_get(Eo *eo_obj EINA_UNUSED, void *_pd, va_list *list) * @param str the string to use. */ static Evas_Object_Text_Item * -_evas_object_text_item_new(Evas_Object *eo_obj, Evas_Object_Text *o, - Evas_Font_Instance *fi, const Eina_Unicode *str, Evas_Script_Type script, - size_t pos, size_t visual_pos, size_t len) +_evas_object_text_item_new(Evas_Object_Protected_Data *obj, + Evas_Object_Text *o, + Evas_Font_Instance *fi, const Eina_Unicode *str, + Evas_Script_Type script, + size_t pos, size_t visual_pos, size_t len) { - Evas_Object_Protected_Data *obj = eo_data_get(eo_obj, EVAS_OBJ_CLASS); Evas_Object_Text_Item *it; it = calloc(1, sizeof(Evas_Object_Text_Item)); @@ -482,13 +497,7 @@ _evas_object_text_item_new(Evas_Object *eo_obj, Evas_Object_Text *o, ENFN->font_text_props_info_create(ENDT, fi, str + pos, &it->text_props, o->bidi_par_props, it->text_pos, len, EVAS_TEXT_PROPS_MODE_SHAPE); - - ENFN->font_string_size_get(ENDT, - o->font, - &it->text_props, - &it->w, &it->h); - it->adv = ENFN->font_h_advance_get(ENDT, o->font, - &it->text_props); + _evas_object_text_item_update_sizes(obj, o, it); } o->items = (Evas_Object_Text_Item *) eina_inlist_append(EINA_INLIST_GET(o->items), EINA_INLIST_GET(it)); @@ -548,6 +557,52 @@ _evas_object_text_item_order(Evas_Object *eo_obj, Evas_Object_Text *o) } } +/** + * Create ellipsis. + */ +static const Eina_Unicode _ellip_str[2] = { 0x2026, '\0' }; + +/* FIXME: We currently leak ellipsis items. */ +static Evas_Object_Text_Item * +_layout_ellipsis_item_new(Evas_Object_Protected_Data *obj, Evas_Object_Text *o, Evas_Object_Text_Item *ti) +{ + Evas_Object_Text_Item *ellip_ti; + size_t len = 1; /* The length of _ellip_str */ + + ellip_ti = _evas_object_text_item_new(obj, o, ti->text_props.font_instance, + _ellip_str, ti->text_props.script, 0, 0, len); + + return ellip_ti; +} + +/* EINA_TRUE if this item is ok and should be included, false if should be + * discarded. */ +static Eina_Bool +_layout_text_item_trim(Evas_Object_Protected_Data *obj, Evas_Object_Text *o, Evas_Object_Text_Item *ti, int idx, Eina_Bool want_start) +{ + Evas_Text_Props new_text_props; + if (idx >= (int) ti->text_props.len) + return EINA_FALSE; + + memset(&new_text_props, 0, sizeof (new_text_props)); + + evas_common_text_props_split(&ti->text_props, &new_text_props, idx); + if (want_start) + { + evas_common_text_props_content_unref(&new_text_props); + } + else + { + evas_common_text_props_content_unref(&ti->text_props); + memcpy(&ti->text_props, &new_text_props, sizeof(ti->text_props)); + ti->text_pos += idx; + ti->visual_pos += idx; + } + _evas_object_text_item_update_sizes(obj, o, ti); + + return EINA_TRUE; +} + /** * @internal * Populates o->items with the items of the text according to text @@ -561,6 +616,7 @@ _evas_object_text_layout(Evas_Object *eo_obj, Evas_Object_Text *o, const Eina_Un { Evas_Object_Protected_Data *obj = eo_data_get(eo_obj, EVAS_OBJ_CLASS); EvasBiDiStrIndex *v_to_l = NULL; + Evas_Coord advance = 0; size_t pos, visual_pos; int len = eina_unicode_strlen(text); #ifdef BIDI_SUPPORT @@ -591,6 +647,7 @@ _evas_object_text_layout(Evas_Object *eo_obj, Evas_Object_Text *o, const Eina_Un while (script_len > 0) { + const Evas_Object_Text_Item *it; Evas_Font_Instance *cur_fi = NULL; int run_len = script_len; if (o->font) @@ -605,20 +662,190 @@ _evas_object_text_layout(Evas_Object *eo_obj, Evas_Object_Text *o, const Eina_Un #else visual_pos = pos; #endif - _evas_object_text_item_new(eo_obj, o, cur_fi, text, script, - pos, visual_pos, run_len); + it = _evas_object_text_item_new(obj, o, cur_fi, text, script, + pos, visual_pos, run_len); + advance += it->adv; pos += run_len; script_len -= run_len; len -= run_len; } } + /* Handle ellipsis */ + if ((o->cur.ellipsis >= 0.0) && (advance > obj->cur.geometry.w) && (obj->cur.geometry.w > 0)) + { + Evas_Coord ellip_frame = obj->cur.geometry.w; + Evas_Object_Text_Item *start_ellip_it = NULL, *end_ellip_it = NULL; + /* Account of the ellipsis item width. As long as ellipsis != 0 + * we have a left ellipsis. And the same with 1 and right. */ + if (o->cur.ellipsis != 0) + { + start_ellip_it = _layout_ellipsis_item_new(obj, o, o->items); + ellip_frame -= start_ellip_it->adv; + } + if (o->cur.ellipsis != 1) + { + /* FIXME: Should take the last item's font and style and etc. *//* weird it's a text, should always have the same style/font */ + end_ellip_it = _layout_ellipsis_item_new(obj, o, o->items); + ellip_frame -= end_ellip_it->adv; + } + + /* The point where we should start from, going for the full + * ellip frame. */ + Evas_Coord ellipsis_coord = o->cur.ellipsis * (advance - ellip_frame); + if (start_ellip_it) + { + Evas_Object_Text_Item *itr = o->items; + advance = 0; + + while (itr && (advance + itr->adv < ellipsis_coord)) + { + Eina_Inlist *itrn = EINA_INLIST_GET(itr)->next; + if ((itr != start_ellip_it) && (itr != end_ellip_it)) + { + advance += itr->adv; + _evas_object_text_item_del(o, itr); + } + itr = (Evas_Object_Text_Item *) itrn; + } + if (itr && (itr != start_ellip_it)) + { + int cut = 1 + ENFN->font_char_at_coords_get(ENDT, + o->font, + &itr->text_props, + ellipsis_coord - advance, + 0, + NULL, NULL, NULL, NULL); + if (cut > 0) + { + start_ellip_it->text_pos = itr->text_pos; + start_ellip_it->visual_pos = itr->visual_pos; + if (!_layout_text_item_trim(obj, o, itr, cut, EINA_FALSE)) + { + _evas_object_text_item_del(o, itr); + } + } + } + + o->items = (Evas_Object_Text_Item *) eina_inlist_remove(EINA_INLIST_GET(o->items), EINA_INLIST_GET(start_ellip_it)); + o->items = (Evas_Object_Text_Item *) eina_inlist_prepend(EINA_INLIST_GET(o->items), EINA_INLIST_GET(start_ellip_it)); + } + + if (end_ellip_it) + { + Evas_Object_Text_Item *itr = o->items; + advance = 0; + + while (itr) + { + if (itr != end_ellip_it) /* was start_ellip_it */ + { + if (advance + itr->adv >= ellip_frame) + { + break; + } + advance += itr->adv; + } + itr = (Evas_Object_Text_Item *) EINA_INLIST_GET(itr)->next; + } + + if (itr == end_ellip_it) + { + /* FIXME: We shouldn't do anything. */ + } + + int cut = ENFN->font_char_at_coords_get(ENDT, + o->font, + &itr->text_props, + ellip_frame - advance, + 0, + NULL, NULL, NULL, NULL); + if (cut >= 0) + { + end_ellip_it->text_pos = itr->text_pos + cut; + end_ellip_it->visual_pos = itr->visual_pos + cut; + if (_layout_text_item_trim(obj, o, itr, cut, EINA_TRUE)) + { + itr = (Evas_Object_Text_Item *) EINA_INLIST_GET(itr)->next; + } + } + + /* Remove the rest of the items */ + while (itr) + { + Eina_Inlist *itrn = EINA_INLIST_GET(itr)->next; + if ((itr != start_ellip_it) && (itr != end_ellip_it)) + _evas_object_text_item_del(o, itr); + itr = (Evas_Object_Text_Item *) itrn; + } + } + } + _evas_object_text_item_order(eo_obj, o); if (v_to_l) free(v_to_l); } +EAPI void +evas_object_text_ellipsis_set(Evas_Object *obj, double ellipsis) +{ + MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ); + return; + MAGIC_CHECK_END(); + eo_do(obj, evas_obj_text_ellipsis_set(ellipsis)); +} + +static void +_text_resize(void *data EINA_UNUSED, + Evas *e EINA_UNUSED, + Evas_Object *obj EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + _evas_object_text_recalc(obj); +} + +static void +_text_ellipsis_set(Eo *eo_obj, void *_pd, va_list *list) +{ + Evas_Object_Protected_Data *obj = eo_data_get(eo_obj, EVAS_OBJ_CLASS); + Evas_Object_Text *o = _pd; + double ellipsis = va_arg(*list, double); + + if (o->cur.ellipsis == ellipsis) return ; + + o->cur.ellipsis = ellipsis; + o->changed = 1; + evas_object_change(eo_obj, obj); + evas_object_clip_dirty(eo_obj, obj); + + evas_object_event_callback_del_full(eo_obj, EVAS_CALLBACK_RESIZE, + _text_resize, o); + evas_object_event_callback_add(eo_obj, EVAS_CALLBACK_RESIZE, + _text_resize, o); +} + +EAPI double +evas_object_text_ellipsis_get(const Evas_Object *obj) +{ + double r = 0; + + MAGIC_CHECK(obj, Evas_Object, MAGIC_OBJ); + return 0; + MAGIC_CHECK_END(); + + eo_do((Eo*) obj, evas_obj_text_ellipsis_get(&r)); + return r; +} + +static void +_text_ellipsis_get(Eo *eo_obj EINA_UNUSED, void *_pd, va_list *list) +{ + const Evas_Object_Text *o = _pd; + double *r = va_arg(*list, double *); + + *r = o->cur.ellipsis; +} EAPI void evas_object_text_text_set(Evas_Object *eo_obj, const char *_text) @@ -1072,10 +1299,13 @@ _text_style_set(Eo *eo_obj, void *_pd, va_list *list) evas_text_style_pad_get(o->cur.style, &pl, &pr, &pt, &pb); o->cur.style = style; evas_text_style_pad_get(o->cur.style, &l, &r, &t, &b); - if (o->items) - obj->cur.geometry.w += (l - pl) + (r - pr); - else - obj->cur.geometry.w = 0; + if (o->cur.ellipsis >= 0) + { + if (o->items) + obj->cur.geometry.w += (l - pl) + (r - pr); + else + obj->cur.geometry.w = 0; + } obj->cur.geometry.h += (t - pt) + (b - pb); evas_object_change(eo_obj, obj); evas_object_clip_dirty(eo_obj, obj); @@ -1540,6 +1770,7 @@ evas_object_text_init(Evas_Object *eo_obj) Evas_Object_Text *o = eo_data_get(eo_obj, MY_CLASS); /* alloc obj private data */ + o->cur.ellipsis = -1.0; o->prev = o->cur; #ifdef BIDI_SUPPORT o->bidi_par_props = evas_bidi_paragraph_props_new(); @@ -1823,7 +2054,6 @@ evas_object_text_render_pre(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj { Evas_Object_Text *o = eo_data_get(eo_obj, MY_CLASS); int is_v, was_v; - /* dont pre-render the obj twice! */ if (obj->pre_render_done) return; obj->pre_render_done = 1; @@ -1839,6 +2069,14 @@ evas_object_text_render_pre(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj evas_object_clip_recalc(obj->cur.eo_clipper, obj->cur.clipper); obj->cur.clipper->func->render_pre(obj->cur.eo_clipper, obj->cur.clipper); } + /* If object size changed and ellipsis is set */ + if ((o->cur.ellipsis >= 0.0 || + o->cur.ellipsis != o->prev.ellipsis) && + ((obj->cur.geometry.w != obj->prev.geometry.w) || + (obj->cur.geometry.h != obj->prev.geometry.h))) + { + _evas_object_text_recalc(eo_obj); + } /* now figure what changed and add draw rects if it just became visible or invisible */ is_v = evas_object_is_visible(eo_obj, obj); @@ -2063,7 +2301,7 @@ _evas_object_text_recalc(Evas_Object *eo_obj) w = _evas_object_text_horiz_advance_get(eo_obj, o); h = _evas_object_text_vert_advance_get(eo_obj, o); evas_text_style_pad_get(o->cur.style, &l, &r, &t, &b); - obj->cur.geometry.w = w + l + r; + obj->cur.geometry.w = w + l + r; obj->cur.geometry.h = h + t + b; //// obj->cur.cache.geometry.validity = 0; } @@ -2072,7 +2310,7 @@ _evas_object_text_recalc(Evas_Object *eo_obj) int t = 0, b = 0; evas_text_style_pad_get(o->cur.style, NULL, NULL, &t, &b); - obj->cur.geometry.w = 0; + obj->cur.geometry.w = 0; obj->cur.geometry.h = o->max_ascent + o->max_descent + t + b; //// obj->cur.cache.geometry.validity = 0; } @@ -2114,6 +2352,8 @@ _class_constructor(Eo_Class *klass) EO_OP_FUNC(EVAS_OBJ_TEXT_ID(EVAS_OBJ_TEXT_SUB_ID_OUTLINE_COLOR_SET), _text_outline_color_set), EO_OP_FUNC(EVAS_OBJ_TEXT_ID(EVAS_OBJ_TEXT_SUB_ID_OUTLINE_COLOR_GET), _text_outline_color_get), EO_OP_FUNC(EVAS_OBJ_TEXT_ID(EVAS_OBJ_TEXT_SUB_ID_STYLE_PAD_GET), _text_style_pad_get), + EO_OP_FUNC(EVAS_OBJ_TEXT_ID(EVAS_OBJ_TEXT_SUB_ID_ELLIPSIS_SET), _text_ellipsis_set), + EO_OP_FUNC(EVAS_OBJ_TEXT_ID(EVAS_OBJ_TEXT_SUB_ID_ELLIPSIS_GET), _text_ellipsis_get), EO_OP_FUNC_SENTINEL }; eo_class_funcs_set(klass, func_desc); @@ -2149,6 +2389,8 @@ static const Eo_Op_Description op_desc[] = { EO_OP_DESCRIPTION(EVAS_OBJ_TEXT_SUB_ID_OUTLINE_COLOR_SET, "Sets the outline color for the given text object."), EO_OP_DESCRIPTION(EVAS_OBJ_TEXT_SUB_ID_OUTLINE_COLOR_GET, "Retrieves the outline color for the given text object."), EO_OP_DESCRIPTION(EVAS_OBJ_TEXT_SUB_ID_STYLE_PAD_GET, "Gets the text style pad of a text object."), + EO_OP_DESCRIPTION(EVAS_OBJ_TEXT_SUB_ID_ELLIPSIS_SET, "Gets the ellipsis of a text object."), + EO_OP_DESCRIPTION(EVAS_OBJ_TEXT_SUB_ID_ELLIPSIS_GET, "Sets the ellipsis of a text object."), EO_OP_DESCRIPTION_SENTINEL }; static const Eo_Class_Description class_desc = {