From 84e3662085fcb4dc7dac51d7a93ce90117391419 Mon Sep 17 00:00:00 2001 From: Daniel Hirt Date: Tue, 8 Dec 2015 17:17:59 +0200 Subject: [PATCH] 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 Change-Id: Ic8285a9ae389e80c09d84558bc0cda749deaa575 --- src/lib/evas/canvas/evas_object_textblock.c | 84 ++++++++++---- src/tests/evas/evas_test_textblock.c | 169 ++++++++++++++++++++++++++++ 2 files changed, 233 insertions(+), 20 deletions(-) diff --git a/src/lib/evas/canvas/evas_object_textblock.c b/src/lib/evas/canvas/evas_object_textblock.c index a78389d..53f4f76 100644 --- a/src/lib/evas/canvas/evas_object_textblock.c +++ b/src/lib/evas/canvas/evas_object_textblock.c @@ -11953,6 +11953,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) { @@ -11995,9 +12028,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; @@ -12008,30 +12044,29 @@ evas_textblock_cursor_range_simple_geometry_get(const Evas_Textblock_Cursor *cur } } - evas_object_geometry_get(cur1->obj, NULL, NULL, &w, NULL); - 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) + if (ln2->items) { - tr->w = tr->x + tr->w - rm; - tr->x = lm; + 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; + } } - else + + /* 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); + /* Fill-in the top line */ + tr = _line_fill_rect_get(ln1, w, lm, rm, EINA_FALSE); + if (tr) { - tr->w = w - tr->x - rm; + rects = eina_list_append(rects, tr); } - 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)); @@ -12041,6 +12076,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); diff --git a/src/tests/evas/evas_test_textblock.c b/src/tests/evas/evas_test_textblock.c index acd76cf..ad6237d 100644 --- a/src/tests/evas/evas_test_textblock.c +++ b/src/tests/evas/evas_test_textblock.c @@ -2575,6 +2575,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, "abc def 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, + "" + "Hello World" + "How are you"); + 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, + "" + "שלום עולם" + "מה שלומך"); + 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, + "" + "שלום עולם" + "מה שלומך"); + 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, + "" + "שלום עולם" + "מה שלומך"); + 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" + "מה שלומך"); + 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русскийעברית"); -- 2.7.4