Evas textblock: improve and fix line range rectangles 33/66933/2
authorDaniel Hirt <daniel.hirt@samsung.com>
Tue, 8 Dec 2015 15:17:59 +0000 (17:17 +0200)
committerYoungbok Shin <youngb.shin@samsung.com>
Mon, 25 Apr 2016 04:25:15 +0000 (21:25 -0700)
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
src/tests/evas/evas_test_textblock.c

index a78389d..53f4f76 100644 (file)
@@ -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);
index acd76cf..ad6237d 100644 (file)
@@ -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, "<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русскийעברית");