forked from enlightenment/efl
Evas textblock: Clean up word/mixed wrap and add liblinebreak support.
SVN revision: 59208
This commit is contained in:
parent
94cb08b3fc
commit
921067869c
|
@ -66,6 +66,10 @@
|
||||||
#include "evas_common.h"
|
#include "evas_common.h"
|
||||||
#include "evas_private.h"
|
#include "evas_private.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_LINEBREAK
|
||||||
|
#include "linebreak.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
/* save typing */
|
/* save typing */
|
||||||
#define ENFN obj->layer->evas->engine.func
|
#define ENFN obj->layer->evas->engine.func
|
||||||
#define ENDT obj->layer->evas->engine.data.output
|
#define ENDT obj->layer->evas->engine.data.output
|
||||||
|
@ -2434,7 +2438,7 @@ _layout_text_cutoff_get(Ctxt *c, Evas_Object_Textblock_Format *fmt,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
* Cut the text up until cut and split
|
* Split before cut, and strip if str[cut - 1] is a whitespace.
|
||||||
*
|
*
|
||||||
* @param c the context to work on - Not NULL.
|
* @param c the context to work on - Not NULL.
|
||||||
* @param ti the item to cut - not null.
|
* @param ti the item to cut - not null.
|
||||||
|
@ -2451,10 +2455,11 @@ _layout_item_text_split_strip_white(Ctxt *c,
|
||||||
size_t cut2;
|
size_t cut2;
|
||||||
|
|
||||||
ts = GET_ITEM_TEXT(ti);
|
ts = GET_ITEM_TEXT(ti);
|
||||||
if (!IS_AT_END(ti, cut) && _is_white(ts[cut]))
|
|
||||||
cut2 = cut + 1;
|
|
||||||
else
|
|
||||||
cut2 = cut;
|
cut2 = cut;
|
||||||
|
/* Also strip the previous white */
|
||||||
|
if ((cut > 1) && _is_white(ts[cut - 1]))
|
||||||
|
cut--;
|
||||||
|
|
||||||
if (!IS_AT_END(ti, cut2) && (ti->text_props.text_len > 0))
|
if (!IS_AT_END(ti, cut2) && (ti->text_props.text_len > 0))
|
||||||
{
|
{
|
||||||
|
@ -2512,108 +2517,6 @@ _layout_item_merge_and_free(Ctxt *c,
|
||||||
_item_free(c->obj, NULL, _ITEM(item2));
|
_item_free(c->obj, NULL, _ITEM(item2));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
* Return the start of the last word up until start.
|
|
||||||
*
|
|
||||||
* @param ti the relevant text item
|
|
||||||
* @param start the start of where to look at.
|
|
||||||
* @return the start of the last word up until start.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
_layout_word_start(const Evas_Object_Textblock_Text_Item *ti, int start)
|
|
||||||
{
|
|
||||||
size_t p, tp;
|
|
||||||
Eina_Unicode chr = 0;
|
|
||||||
const Eina_Unicode *str = GET_ITEM_TEXT(ti);
|
|
||||||
|
|
||||||
p = start;
|
|
||||||
chr = GET_NEXT(str, ti, p);
|
|
||||||
if (_is_white(chr))
|
|
||||||
{
|
|
||||||
tp = p;
|
|
||||||
while (_is_white(chr))
|
|
||||||
{
|
|
||||||
tp = p;
|
|
||||||
chr = GET_NEXT(str, ti, p);
|
|
||||||
}
|
|
||||||
return tp;
|
|
||||||
}
|
|
||||||
p = start;
|
|
||||||
tp = p;
|
|
||||||
while (p > 0)
|
|
||||||
{
|
|
||||||
chr = GET_PREV(str, p);
|
|
||||||
if (_is_white(chr)) break;
|
|
||||||
tp = p;
|
|
||||||
}
|
|
||||||
if (_is_white(chr))
|
|
||||||
{
|
|
||||||
GET_NEXT(str, ti, p);
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
* returns the index of the words end starting from p
|
|
||||||
*
|
|
||||||
* @param ti the relevant text item
|
|
||||||
* @param p start position - must be within strings range..
|
|
||||||
*
|
|
||||||
* @return the position of the end of the word. -1 on error.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
_layout_word_end(const Evas_Object_Textblock_Text_Item *ti, int p)
|
|
||||||
{
|
|
||||||
size_t tp;
|
|
||||||
Eina_Unicode ch;
|
|
||||||
const Eina_Unicode *str = GET_ITEM_TEXT(ti);
|
|
||||||
|
|
||||||
tp = p;
|
|
||||||
ch = GET_NEXT(str, ti, tp);
|
|
||||||
while ((!_is_white(ch)) && (ch != 0))
|
|
||||||
{
|
|
||||||
p = tp;
|
|
||||||
ch = GET_NEXT(str, ti, tp);
|
|
||||||
}
|
|
||||||
if (ch == 0) return -1;
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
* returns the index of the start of the next word.
|
|
||||||
*
|
|
||||||
* @param ti the relevant text item
|
|
||||||
* @param p start position - must be within strings range..
|
|
||||||
*
|
|
||||||
* @return the position of the start of the next word. -1 on error.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
_layout_word_next(const Evas_Object_Textblock_Text_Item *ti, int p)
|
|
||||||
{
|
|
||||||
size_t tp;
|
|
||||||
Eina_Unicode ch;
|
|
||||||
const Eina_Unicode *str = GET_ITEM_TEXT(ti);
|
|
||||||
|
|
||||||
tp = p;
|
|
||||||
ch = GET_NEXT(str, ti, tp);
|
|
||||||
while ((!_is_white(ch)) && (ch != 0))
|
|
||||||
{
|
|
||||||
p = tp;
|
|
||||||
ch = GET_NEXT(str, ti, tp);
|
|
||||||
}
|
|
||||||
if (ch == 0) return -1;
|
|
||||||
while ((_is_white(ch)) && (ch != 0))
|
|
||||||
{
|
|
||||||
p = tp;
|
|
||||||
ch = GET_NEXT(str, ti, tp);
|
|
||||||
}
|
|
||||||
if (ch == 0) return -1;
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
* Calculates an item's size.
|
* Calculates an item's size.
|
||||||
|
@ -3100,10 +3003,13 @@ _layout_update_par(Ctxt *c)
|
||||||
/* -1 means no wrap */
|
/* -1 means no wrap */
|
||||||
static int
|
static int
|
||||||
_layout_get_charwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
|
_layout_get_charwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
|
||||||
const Evas_Object_Textblock_Text_Item *ti)
|
const Evas_Object_Textblock_Text_Item *ti, const char *breaks)
|
||||||
{
|
{
|
||||||
int wrap;
|
int wrap;
|
||||||
const Eina_Unicode *str = GET_ITEM_TEXT(ti);
|
const Eina_Unicode *str = GET_ITEM_TEXT(ti);
|
||||||
|
/* Currently not being used, because it doesn't contain relevant
|
||||||
|
* information */
|
||||||
|
(void) breaks;
|
||||||
|
|
||||||
wrap = _layout_text_cutoff_get(c, fmt, ti);
|
wrap = _layout_text_cutoff_get(c, fmt, ti);
|
||||||
if (wrap == 0)
|
if (wrap == 0)
|
||||||
|
@ -3115,75 +3021,74 @@ _layout_get_charwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -1 means no wrap */
|
/* -1 means no wrap */
|
||||||
|
#ifdef HAVE_LINEBREAK
|
||||||
|
|
||||||
|
/* Allow break means: if we can break after the current char */
|
||||||
|
#define ALLOW_BREAK(i) \
|
||||||
|
((((i) + ti->parent.text_pos) > 0) ? \
|
||||||
|
(breaks[ti->parent.text_pos + (i)] == LINEBREAK_ALLOWBREAK) : \
|
||||||
|
(EINA_FALSE))
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define ALLOW_BREAK(i) \
|
||||||
|
((((i) + ti->parent.text_pos) > 0) ? \
|
||||||
|
(_is_white(str[(i)])) : \
|
||||||
|
(EINA_FALSE))
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
static int
|
static int
|
||||||
_layout_get_word_mixwrap_common(Ctxt *c, Evas_Object_Textblock_Format *fmt,
|
_layout_get_word_mixwrap_common(Ctxt *c, Evas_Object_Textblock_Format *fmt,
|
||||||
const Evas_Object_Textblock_Text_Item *ti, Eina_Bool mixed_wrap)
|
const Evas_Object_Textblock_Text_Item *ti, Eina_Bool mixed_wrap,
|
||||||
|
const char *breaks)
|
||||||
{
|
{
|
||||||
int wrap = -1, twrap;
|
int wrap = -1;
|
||||||
int orig_wrap;
|
int orig_wrap;
|
||||||
Eina_Unicode ch;
|
|
||||||
const Eina_Unicode *str = GET_ITEM_TEXT(ti);
|
const Eina_Unicode *str = GET_ITEM_TEXT(ti);
|
||||||
|
#ifndef HAVE_LINEBREAK
|
||||||
|
/* Not used without liblinebreak ATM. */
|
||||||
|
(void) breaks;
|
||||||
|
#endif
|
||||||
|
|
||||||
wrap = _layout_text_cutoff_get(c, fmt, ti);
|
wrap = _layout_text_cutoff_get(c, fmt, ti);
|
||||||
/* Avoiding too small textblocks to even contain one char */
|
/* Avoiding too small textblocks to even contain one char.
|
||||||
if (wrap == 0)
|
* FIXME: This can cause breaking inside ligatures. */
|
||||||
GET_NEXT(str, ti, wrap);
|
|
||||||
orig_wrap = wrap;
|
orig_wrap = wrap;
|
||||||
/* We need to wrap and found the position that overflows */
|
|
||||||
if (wrap > 0)
|
if (wrap > 0)
|
||||||
{
|
{
|
||||||
size_t index = (size_t) wrap;
|
/* The wrapping point found is the first char of the next string
|
||||||
ch = GET_NEXT(str, ti, index);
|
the rest works on the last char of the previous string.
|
||||||
if (!_is_white(ch))
|
If it's a whitespace, then it's ok, and no need to go back
|
||||||
wrap = _layout_word_start(ti, wrap);
|
because we'll remove it anyway. */
|
||||||
/* If we found where to cut the text at, i.e the start
|
if (!_is_white(str[wrap]))
|
||||||
* of the word we were pointing at */
|
GET_PREV(str, wrap);
|
||||||
if (wrap > 0)
|
/* If there's a breakable point inside the text, scan backwards until
|
||||||
|
* we find it */
|
||||||
|
do
|
||||||
{
|
{
|
||||||
twrap = wrap;
|
if (ALLOW_BREAK(wrap))
|
||||||
ch = GET_PREV(str, twrap);
|
break;
|
||||||
/* the text intersects the wrap point on a whitespace char */
|
wrap--;
|
||||||
if (_is_white(ch))
|
}
|
||||||
|
while (wrap >= 0);
|
||||||
|
|
||||||
|
if (wrap >= 0)
|
||||||
{
|
{
|
||||||
|
/* We found a suitable wrapping point, break here. */
|
||||||
|
GET_NEXT(str, ti, wrap);
|
||||||
return wrap;
|
return wrap;
|
||||||
}
|
}
|
||||||
/* intersects a word */
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* walk back to start of word */
|
|
||||||
twrap = _layout_word_start(ti, wrap);
|
|
||||||
if (twrap > 0)
|
|
||||||
{
|
|
||||||
wrap = twrap;
|
|
||||||
ch = GET_PREV(str, wrap);
|
|
||||||
return (!IS_AT_END(ti, (size_t) wrap)) ? wrap : -1;
|
|
||||||
}
|
|
||||||
else if (twrap < 0)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* If we weren't able to find the start of the word we
|
|
||||||
* are currently pointing at, or we were able but it's
|
|
||||||
* the first word - the end of this word is the wrap point, o */
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* wrap now is the index of the word START */
|
|
||||||
index = wrap;
|
|
||||||
ch = GET_NEXT(str, ti, index);
|
|
||||||
|
|
||||||
/* If there are already items in this line, we
|
/* If there are already items in this line, we
|
||||||
* should just try creating a new line for it */
|
* should just try creating a new line for it */
|
||||||
if (c->ln->items)
|
if (c->ln->items)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/* If there were no items in this line, try to do
|
|
||||||
* our best wrapping possible since it's the middle
|
|
||||||
* of the word */
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (mixed_wrap)
|
if (mixed_wrap)
|
||||||
{
|
{
|
||||||
return ((orig_wrap >= 0) &&
|
return ((orig_wrap >= 0) &&
|
||||||
|
@ -3191,13 +3096,21 @@ _layout_get_word_mixwrap_common(Ctxt *c, Evas_Object_Textblock_Format *fmt,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
wrap = 0;
|
/* Scan forward to find the next wrapping point */
|
||||||
twrap = _layout_word_end(ti, wrap);
|
wrap = orig_wrap;
|
||||||
wrap = twrap;
|
do
|
||||||
if (twrap >= 0)
|
|
||||||
{
|
{
|
||||||
ch = GET_NEXT(str, ti, wrap);
|
if (ALLOW_BREAK(wrap))
|
||||||
return (!IS_AT_END(ti, (size_t) wrap)) ? wrap : -1;
|
break;
|
||||||
|
wrap++;
|
||||||
|
}
|
||||||
|
while (!IS_AT_END(ti, (size_t) wrap));
|
||||||
|
|
||||||
|
|
||||||
|
if (!IS_AT_END(ti, (size_t) wrap))
|
||||||
|
{
|
||||||
|
GET_NEXT(str, ti, wrap);
|
||||||
|
return wrap;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -3205,56 +3118,32 @@ _layout_get_word_mixwrap_common(Ctxt *c, Evas_Object_Textblock_Format *fmt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
/* We need to wrap, but for some reason we failed obatining the
|
|
||||||
* overflow position. */
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/*FIXME: sanitize this error handling - should probably
|
|
||||||
* never get here anyway unless something really bad
|
|
||||||
* has happend */
|
|
||||||
/* wrap now is the index of the word START */
|
|
||||||
if (wrap < 0) wrap = 0;
|
|
||||||
|
|
||||||
/* If there are already items in the line, break before. */
|
}
|
||||||
if (c->ln->items)
|
else if (wrap == 0)
|
||||||
{
|
{
|
||||||
return 0;
|
/* Just to avoid too small textblocks */
|
||||||
}
|
GET_NEXT(str, ti, wrap);
|
||||||
else
|
return wrap;
|
||||||
{
|
|
||||||
twrap = _layout_word_end(ti, wrap);
|
|
||||||
wrap = _layout_word_next(ti, wrap);
|
|
||||||
if (wrap >= 0)
|
|
||||||
{
|
|
||||||
ch = GET_NEXT(str, ti, wrap);
|
|
||||||
return (!IS_AT_END(ti, (size_t) wrap)) ? wrap : -1;
|
|
||||||
}
|
|
||||||
else if (twrap >= 0)
|
|
||||||
{
|
|
||||||
ch = GET_NEXT(str, ti, twrap);
|
|
||||||
return (!IS_AT_END(ti, (size_t) twrap)) ? twrap : -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -1 means no wrap */
|
/* -1 means no wrap */
|
||||||
static int
|
static int
|
||||||
_layout_get_wordwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
|
_layout_get_wordwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
|
||||||
const Evas_Object_Textblock_Text_Item *ti)
|
const Evas_Object_Textblock_Text_Item *ti, const char *breaks)
|
||||||
{
|
{
|
||||||
return _layout_get_word_mixwrap_common(c, fmt, ti, EINA_FALSE);
|
return _layout_get_word_mixwrap_common(c, fmt, ti, EINA_FALSE, breaks);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -1 means no wrap */
|
/* -1 means no wrap */
|
||||||
static int
|
static int
|
||||||
_layout_get_mixedwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
|
_layout_get_mixedwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
|
||||||
const Evas_Object_Textblock_Text_Item *ti)
|
const Evas_Object_Textblock_Text_Item *ti, const char *breaks)
|
||||||
{
|
{
|
||||||
return _layout_get_word_mixwrap_common(c, fmt, ti, EINA_TRUE);
|
return _layout_get_word_mixwrap_common(c, fmt, ti, EINA_TRUE, breaks);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Should be moved inside _layout_ellipsis_item_new once we fix the hack in
|
/* Should be moved inside _layout_ellipsis_item_new once we fix the hack in
|
||||||
|
@ -3303,6 +3192,9 @@ _layout_visualize_par(Ctxt *c)
|
||||||
{
|
{
|
||||||
Evas_Object_Textblock_Item *it;
|
Evas_Object_Textblock_Item *it;
|
||||||
Eina_List *i;
|
Eina_List *i;
|
||||||
|
int ret = 0;
|
||||||
|
char *line_breaks = NULL;
|
||||||
|
|
||||||
if (!c->par->logical_items)
|
if (!c->par->logical_items)
|
||||||
return 2;
|
return 2;
|
||||||
|
|
||||||
|
@ -3452,7 +3344,8 @@ _layout_visualize_par(Ctxt *c)
|
||||||
c->ln->ellip_ti = ellip_ti;
|
c->ln->ellip_ti = ellip_ti;
|
||||||
_layout_line_finalize(c, ellip_ti->parent.format);
|
_layout_line_finalize(c, ellip_ti->parent.format);
|
||||||
|
|
||||||
return 1;
|
ret = 1;
|
||||||
|
goto end;
|
||||||
}
|
}
|
||||||
else if (it->format->wrap_word || it->format->wrap_char ||
|
else if (it->format->wrap_word || it->format->wrap_char ||
|
||||||
it->format->wrap_mixed)
|
it->format->wrap_mixed)
|
||||||
|
@ -3472,13 +3365,36 @@ _layout_visualize_par(Ctxt *c)
|
||||||
Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
|
Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
|
||||||
int wrap;
|
int wrap;
|
||||||
|
|
||||||
|
#ifdef HAVE_LINEBREAK
|
||||||
|
/* If we haven't calculated the linebreaks yet,
|
||||||
|
* do */
|
||||||
|
if (!line_breaks)
|
||||||
|
{
|
||||||
|
size_t len =
|
||||||
|
eina_ustrbuf_length_get(it->text_node->unicode);
|
||||||
|
line_breaks = malloc(len);
|
||||||
|
/* Only relevant in those cases */
|
||||||
|
if (it->format->wrap_word || it->format->wrap_mixed)
|
||||||
|
{
|
||||||
|
set_linebreaks_utf32((const utf32_t *)
|
||||||
|
eina_ustrbuf_string_get(
|
||||||
|
it->text_node->unicode),
|
||||||
|
len, "", line_breaks);
|
||||||
|
/* FIXME: "" should be text_props language */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
adv_line = 1;
|
adv_line = 1;
|
||||||
if (it->format->wrap_word)
|
if (it->format->wrap_word)
|
||||||
wrap = _layout_get_wordwrap(c, it->format, ti);
|
wrap = _layout_get_wordwrap(c, it->format, ti,
|
||||||
|
line_breaks);
|
||||||
else if (it->format->wrap_char)
|
else if (it->format->wrap_char)
|
||||||
wrap = _layout_get_charwrap(c, it->format, ti);
|
wrap = _layout_get_charwrap(c, it->format, ti,
|
||||||
|
line_breaks);
|
||||||
else if (it->format->wrap_mixed)
|
else if (it->format->wrap_mixed)
|
||||||
wrap = _layout_get_mixedwrap(c, it->format, ti);
|
wrap = _layout_get_mixedwrap(c, it->format, ti,
|
||||||
|
line_breaks);
|
||||||
else
|
else
|
||||||
wrap = -1;
|
wrap = -1;
|
||||||
|
|
||||||
|
@ -3536,7 +3452,14 @@ _layout_visualize_par(Ctxt *c)
|
||||||
/* Here 'it' is the last format used */
|
/* Here 'it' is the last format used */
|
||||||
_layout_line_finalize(c, it->format);
|
_layout_line_finalize(c, it->format);
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
|
end:
|
||||||
|
#ifdef HAVE_LINEBREAK
|
||||||
|
if (line_breaks)
|
||||||
|
free(line_breaks);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7905,6 +7828,14 @@ static void
|
||||||
evas_object_textblock_init(Evas_Object *obj)
|
evas_object_textblock_init(Evas_Object *obj)
|
||||||
{
|
{
|
||||||
Evas_Object_Textblock *o;
|
Evas_Object_Textblock *o;
|
||||||
|
#ifdef HAVE_LINEBREAK
|
||||||
|
static Eina_Bool linebreak_init = EINA_FALSE;
|
||||||
|
if (!linebreak_init)
|
||||||
|
{
|
||||||
|
linebreak_init = EINA_TRUE;
|
||||||
|
init_linebreak();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* alloc image ob, setup methods and default values */
|
/* alloc image ob, setup methods and default values */
|
||||||
obj->object_data = evas_object_textblock_new();
|
obj->object_data = evas_object_textblock_new();
|
||||||
|
|
Loading…
Reference in New Issue