Evas textblock: improve and fix line range rectangles

The line range rectangles geometries needed a bit of adjusting. I
started out with fixing T2648. In order to fill the gap between the end
of the line and the margins, the geometry of the last line's character
was used. I am not really sure why. Anyway, we have the line geometry,
so I replaced it with that.

Then, it led me to do some alignment checks, and indeed alignment cases
were not treated. For instance, an LTR paragraph could have a line
aligned with a value greater than 0.0. That means that we should fill
the gap from the left of the line, if it was the last line in a
multi-line selection. The inverse case is for RTL.

I think it now works as it should. Will see if the selection logic is
missing some more stuff once I come up with more example cases.

Fixes T2648.

@fix
This commit is contained in:
Daniel Hirt 2015-12-08 17:17:59 +02:00
parent 326d0e548d
commit 7e8e392db6
2 changed files with 234 additions and 21 deletions

View File

@ -11212,6 +11212,39 @@ _evas_textblock_cursor_range_in_line_geometry_get(
return rects;
}
/* Helper that creates a selection rectangle to a given line.
* The given 'inv' indicates an inverse behavior. */
static Evas_Textblock_Rectangle *
_line_fill_rect_get(const Evas_Object_Textblock_Line *ln,
Evas_Coord w, Evas_Coord lm, Evas_Coord rm, Eina_Bool inv)
{
Evas_Textblock_Rectangle *tr;
tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
tr->y = ln->par->y + ln->y;
tr->h = ln->h;
//Reminder: ln->x includes the left margin */
if ((!inv && (ln->par->direction == EVAS_BIDI_DIRECTION_RTL)) ||
(inv && (ln->par->direction != EVAS_BIDI_DIRECTION_RTL)))
{
tr->x = lm;
tr->w = ln->x - lm;
}
else
{
tr->x = ln->x + ln->w;
tr->w = w - rm - tr->x;
}
if (tr->w == 0)
{
free(tr);
tr = NULL;
}
return tr;
}
EAPI Eina_Iterator *
evas_textblock_cursor_range_simple_geometry_get(const Evas_Textblock_Cursor *cur1, const Evas_Textblock_Cursor *cur2)
{
@ -11254,9 +11287,12 @@ evas_textblock_cursor_range_simple_geometry_get(const Evas_Textblock_Cursor *cur
int lm = 0, rm = 0;
Eina_List *rects2 = NULL;
Evas_Coord w;
Evas_Textblock_Cursor *tc;
Evas_Textblock_Rectangle *tr;
evas_object_geometry_get(cur1->obj, NULL, NULL, &w, NULL);
/* Use the minimum left margin and right margin for a uniform
* line coverage of the rectangles */
if (ln1->items)
{
Evas_Object_Textblock_Format *fm = ln1->items->format;
@ -11267,30 +11303,29 @@ evas_textblock_cursor_range_simple_geometry_get(const Evas_Textblock_Cursor *cur
}
}
evas_object_geometry_get(cur1->obj, NULL, NULL, &w, NULL);
if (ln2->items)
{
Evas_Object_Textblock_Format *fm = ln2->items->format;
if (fm)
{
if (fm->margin.l < lm) lm = fm->margin.l;
if (fm->margin.r < rm) rm = fm->margin.r;
}
}
/* Append the rectangles by visual order (top, middle, bottom). Keep
* it like that so it is also easier to test and debug. */
/* Top line */
rects = _evas_textblock_cursor_range_in_line_geometry_get(ln1, cur1, NULL);
/* Extend selection rectangle in first line */
tc = evas_object_textblock_cursor_new(cur1->obj);
evas_textblock_cursor_copy(cur1, tc);
evas_textblock_cursor_line_char_last(tc);
tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
evas_textblock_cursor_pen_geometry_get(tc, &tr->x, &tr->y, &tr->w, &tr->h);
if (ln1->par->direction == EVAS_BIDI_DIRECTION_RTL)
/* Fill-in the top line */
tr = _line_fill_rect_get(ln1, w, lm, rm, EINA_FALSE);
if (tr)
{
tr->w = tr->x + tr->w - rm;
tr->x = lm;
rects = eina_list_append(rects, tr);
}
else
{
tr->w = w - tr->x - rm;
}
rects = eina_list_append(rects, tr);
evas_textblock_cursor_free(tc);
rects2 = _evas_textblock_cursor_range_in_line_geometry_get(ln2, NULL, cur2);
/* Add middle rect */
/* Middle rect (lines) */
if ((ln1->par->y + ln1->y + ln1->h) != (ln2->par->y + ln2->y))
{
tr = calloc(1, sizeof(Evas_Textblock_Rectangle));
@ -11300,6 +11335,15 @@ evas_textblock_cursor_range_simple_geometry_get(const Evas_Textblock_Cursor *cur
tr->h = ln2->par->y + ln2->y - tr->y;
rects = eina_list_append(rects, tr);
}
/* Bottom line */
rects2 = _evas_textblock_cursor_range_in_line_geometry_get(ln2, NULL, cur2);
/* Fill-in the bottom line */
tr = _line_fill_rect_get(ln2, w, lm, rm, EINA_TRUE);
if (tr)
{
rects2 = eina_list_append(rects2, tr);
}
rects = eina_list_merge(rects, rects2);
}
itr = _evas_textblock_selection_iterator_new(rects);

View File

@ -2560,6 +2560,175 @@ START_TEST(evas_textblock_geometries)
eina_iterator_free(it);
eina_iterator_free(it2);
/* Check number of rectangles in case a of line wrapping */
evas_object_textblock_text_markup_set(tb, "<wrap=word>abc def <color=#0ff>ghi");
evas_object_resize(tb, w, w / 2);
evas_textblock_cursor_pos_set(cur, 10);
{
Evas_Coord cx;
evas_textblock_cursor_geometry_bidi_get(cur, &cx, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
EVAS_TEXTBLOCK_CURSOR_BEFORE);
/* enforce wrapping of "ghi" to the next line */
evas_object_resize(tb, cx, 400);
/* Sanity, check there is actually a second line */
fail_if (!evas_textblock_cursor_line_set(cur, 1));
fail_if (evas_textblock_cursor_line_set(cur, 2));
}
evas_textblock_cursor_pos_set(cur, 7);
evas_textblock_cursor_pos_set(main_cur, 9);
it = evas_textblock_cursor_range_simple_geometry_get(cur, main_cur);
fail_if(!it);
rects = eina_iterator_container_get(it);
fail_if(!rects);
ck_assert_int_eq(eina_list_count(rects), 3);
{
Evas_Coord y1, y2;
void *tmp = tr;
/* We have 3 rectangles */
Eina_Iterator *itr = it;
fail_if (!eina_iterator_next(itr, &tmp));
tr = tmp;
y1 = tr->y;
fail_if (!eina_iterator_next(itr, &tmp));
tr = tmp;
y2 = tr->y;
/* Basically it means that the "extending" rectangle should not somehow
* reach the second line in this example. */
ck_assert_int_eq(y1, y2);
eina_iterator_free(it);
}
/* Alignment fills */
/* LTR text */
evas_object_resize(tb, 400, 400);
evas_object_textblock_text_markup_set(tb,
"<align=0.1>"
"Hello World<ps>"
"How are you<ps>");
evas_textblock_cursor_line_set(cur, 0);
evas_textblock_cursor_line_set(main_cur, 1);
it = evas_textblock_cursor_range_simple_geometry_get(cur, main_cur);
fail_if(!it);
rects = eina_iterator_container_get(it);
fail_if(!rects);
ck_assert_int_eq(eina_list_count(rects), 3);
evas_textblock_cursor_char_next(main_cur);
eina_iterator_free(it);
evas_textblock_cursor_char_next(main_cur);
it = evas_textblock_cursor_range_simple_geometry_get(cur, main_cur);
fail_if(!it);
rects = eina_iterator_container_get(it);
fail_if(!rects);
ck_assert_int_eq(eina_list_count(rects), 4);
evas_textblock_cursor_char_next(main_cur);
eina_iterator_free(it);
evas_textblock_cursor_line_set(main_cur, 2);
evas_textblock_cursor_char_next(main_cur);
it = evas_textblock_cursor_range_simple_geometry_get(cur, main_cur);
fail_if(!it);
rects = eina_iterator_container_get(it);
fail_if(!rects);
ck_assert_int_eq(eina_list_count(rects), 4);
evas_textblock_cursor_char_next(main_cur);
eina_iterator_free(it);
/* RTL text aligned to the left */
evas_object_textblock_text_markup_set(tb,
"<align=left>"
"שלום עולם<ps>"
"מה שלומך");
evas_textblock_cursor_line_set(cur, 0);
evas_textblock_cursor_line_set(main_cur, 1);
it = evas_textblock_cursor_range_simple_geometry_get(cur, main_cur);
fail_if(!it);
rects = eina_iterator_container_get(it);
fail_if(!rects);
ck_assert_int_eq(eina_list_count(rects), 2);
evas_textblock_cursor_char_next(main_cur);
eina_iterator_free(it);
evas_textblock_cursor_char_next(main_cur);
it = evas_textblock_cursor_range_simple_geometry_get(cur, main_cur);
fail_if(!it);
rects = eina_iterator_container_get(it);
fail_if(!rects);
ck_assert_int_eq(eina_list_count(rects), 3);
evas_textblock_cursor_char_next(main_cur);
eina_iterator_free(it);
/* RTL text aligned to the middle */
evas_object_textblock_text_markup_set(tb,
"<align=middle>"
"שלום עולם<ps>"
"מה שלומך");
evas_textblock_cursor_line_set(cur, 0);
evas_textblock_cursor_line_set(main_cur, 1);
it = evas_textblock_cursor_range_simple_geometry_get(cur, main_cur);
fail_if(!it);
rects = eina_iterator_container_get(it);
fail_if(!rects);
ck_assert_int_eq(eina_list_count(rects), 3);
eina_iterator_free(it);
evas_textblock_cursor_char_next(main_cur);
it = evas_textblock_cursor_range_simple_geometry_get(cur, main_cur);
fail_if(!it);
rects = eina_iterator_container_get(it);
fail_if(!rects);
ck_assert_int_eq(eina_list_count(rects), 4);
eina_iterator_free(it);
evas_object_textblock_text_markup_set(tb,
"<align=middle>"
"שלום עולם<ps>"
"מה שלומך");
evas_textblock_cursor_line_set(cur, 0);
evas_textblock_cursor_line_set(main_cur, 1);
it = evas_textblock_cursor_range_simple_geometry_get(cur, main_cur);
fail_if(!it);
rects = eina_iterator_container_get(it);
fail_if(!rects);
ck_assert_int_eq(eina_list_count(rects), 3);
eina_iterator_free(it);
evas_textblock_cursor_char_next(main_cur);
it = evas_textblock_cursor_range_simple_geometry_get(cur, main_cur);
fail_if(!it);
rects = eina_iterator_container_get(it);
fail_if(!rects);
ck_assert_int_eq(eina_list_count(rects), 4);
eina_iterator_free(it);
/* Auto align RTL and LTR */
evas_object_textblock_text_markup_set(tb,
"Hello world<ps>"
"מה שלומך");
evas_textblock_cursor_line_set(cur, 0);
evas_textblock_cursor_line_set(main_cur, 1);
it = evas_textblock_cursor_range_simple_geometry_get(cur, main_cur);
fail_if(!it);
rects = eina_iterator_container_get(it);
fail_if(!rects);
ck_assert_int_eq(eina_list_count(rects), 2);
eina_iterator_free(it);
evas_textblock_cursor_char_next(main_cur);
it = evas_textblock_cursor_range_simple_geometry_get(cur, main_cur);
fail_if(!it);
rects = eina_iterator_container_get(it);
fail_if(!rects);
ck_assert_int_eq(eina_list_count(rects), 3);
eina_iterator_free(it);
/* Same run different scripts */
evas_object_textblock_text_markup_set(tb, "עבריתenglishрусскийעברית");