Split logical layouting and visual layouting completely. There are still some bugs...
authortasn <tasn@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Sun, 30 Jan 2011 10:40:30 +0000 (10:40 +0000)
committertasn <tasn@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Sun, 30 Jan 2011 10:40:30 +0000 (10:40 +0000)
git-svn-id: svn+ssh://svn.enlightenment.org/var/svn/e/trunk/evas@56492 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33

src/lib/canvas/evas_object_textblock.c
src/lib/engines/common/evas_font_ot.c
src/lib/engines/common/evas_font_ot.h
src/lib/engines/common/evas_font_query.c
src/lib/engines/common/evas_text_utils.c
src/lib/engines/common/evas_text_utils.h

index 960363f..c3c2392 100644 (file)
@@ -323,6 +323,13 @@ struct _Evas_Object_Textblock_Item
 #endif
    Evas_Coord                           adv, x, w, h;
    Evas_Text_Props                      text_props;
+   Eina_Bool                            merge : 1; /* Indicates whether this
+                                                      item should merge to the
+                                                      previous item or not */
+   Eina_Bool                            visually_deleted : 1;
+                                                   /* Indicates whether this
+                                                      item is used in the visual
+                                                      layout or not. */
 };
 
 struct _Evas_Object_Textblock_Text_Item
@@ -1758,6 +1765,7 @@ struct _Ctxt
    Eina_Bool align_auto;
 };
 
+static void _layout_text_add_logical_item(Ctxt *c, Evas_Object_Textblock_Format *fmt, Evas_Object_Textblock_Text_Item *ti, const Evas_Object_Textblock_Item *rel);
 /**
  * @internal
  * Adjust the ascent/descent of the format and context.
@@ -2320,6 +2328,66 @@ _layout_item_text_cutoff(Ctxt *c __UNUSED__, Evas_Object_Textblock_Text_Item *ti
 
 /**
  * @internal
+ * Cut the text up until cut and split
+ *
+ * @param c the context to work on - Not NULL.
+ * @param it the item to cut - not null.
+ * @param cut the cut index.
+ * @return the second (newly created) item.
+ */
+static Evas_Object_Textblock_Text_Item *
+_layout_item_text_split_strip_white(Ctxt *c,
+      Evas_Object_Textblock_Text_Item *ti, int cut)
+{
+   Eina_Unicode *ts;
+   Evas_Object_Textblock_Text_Item *new_ti;
+   int cut2;
+
+   ts = ti->text;
+   if (_is_white(ts[cut]))
+     cut2 = cut + 1;
+   else
+     cut2 = cut;
+
+   new_ti = _layout_text_item_new(c, ti->format, &ts[cut2]);
+   new_ti->parent.text_node = ti->parent.text_node;
+   new_ti->parent.text_pos = ti->parent.text_pos + cut2;
+   new_ti->parent.merge = EINA_TRUE;
+   ts[cut2] = 0;
+
+   evas_common_text_props_split(&ti->parent.text_props,
+         &new_ti->parent.text_props, cut2);
+   _layout_text_add_logical_item(c, ti->format, new_ti, _ITEM(ti));
+
+   /* FIXME: Will break with kerning and a bunch of other stuff, should
+    * maybe adjust the last adv of the prev and the offset of the cur*/
+   ti->parent.w -= new_ti->parent.w;
+   ti->parent.adv -= new_ti->parent.adv;
+
+   if (cut2 > cut)
+     {
+        Evas_Object_Textblock_Text_Item *white_ti;
+        white_ti = _layout_text_item_new(c, ti->format, &ts[cut]);
+        white_ti->parent.text_node = ti->parent.text_node;
+        white_ti->parent.text_pos = ti->parent.text_pos + cut;
+        white_ti->parent.merge = EINA_TRUE;
+        white_ti->parent.visually_deleted = EINA_TRUE;
+        ts[cut] = 0;
+
+        evas_common_text_props_split(&ti->parent.text_props,
+              &white_ti->parent.text_props, cut);
+        _layout_text_add_logical_item(c, ti->format, white_ti, _ITEM(ti));
+        ti->parent.w -= white_ti->parent.w;
+        ti->parent.adv -= white_ti->parent.adv;
+     }
+
+   ti->text = eina_unicode_strdup(ts);
+   free(ts);
+   return new_ti;
+}
+
+/**
+ * @internal
  * Return the start of the last word up until start.
  *
  * @param str the string to work on.
@@ -2488,10 +2556,12 @@ _layout_word_next(Eina_Unicode *str, int p)
  * @param c the context
  * @param fmt the format of the item.
  * @param it the item itself.
+ * @param rel item ti will be appened after, NULL = last.
  */
 static void
 _layout_text_add_logical_item(Ctxt *c, Evas_Object_Textblock_Format *fmt,
-      Evas_Object_Textblock_Text_Item *ti)
+      Evas_Object_Textblock_Text_Item *ti,
+      const Evas_Object_Textblock_Item *rel)
 {
    int tw, th, adv, inset;
 
@@ -2512,7 +2582,8 @@ _layout_text_add_logical_item(Ctxt *c, Evas_Object_Textblock_Format *fmt,
            ti->text, &ti->parent.text_props);
    ti->parent.adv = adv;
    ti->parent.x = 0;
-   c->par->logical_items = eina_list_append(c->par->logical_items, ti);
+   c->par->logical_items = eina_list_append_relative(
+         c->par->logical_items, ti, rel);
 }
 
 /**
@@ -2632,7 +2703,7 @@ skip:
           }
         str += tmp_len;
 
-        _layout_text_add_logical_item(c, fmt, ti);
+        _layout_text_add_logical_item(c, fmt, ti, NULL);
      }
 
    if (alloc_str) free(alloc_str);
@@ -2872,6 +2943,128 @@ _layout_update_par(Ctxt *c)
         c->par->y = last_par->y + last_par->h;
      }
 }
+
+/* -1 means no wrap */
+static int
+_layout_get_charwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
+      const Evas_Object_Textblock_Text_Item *ti)
+{
+   int wrap;
+
+   wrap = _layout_text_cutoff_get(c, fmt, ti);
+   if (wrap == 0)
+     GET_NEXT(ti->text, wrap);
+
+   return wrap;
+}
+
+/* -1 means no wrap */
+static int
+_layout_get_wordwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
+      const Evas_Object_Textblock_Text_Item *ti)
+{
+   int wrap = -1, twrap;
+   Eina_Unicode ch;
+   const Eina_Unicode *str = ti->text;
+
+   wrap = _layout_text_cutoff_get(c, fmt, ti);
+   /* Avoiding too small textblocks to even contain one char */
+   if (wrap == 0)
+     GET_NEXT(str, wrap);
+   /* We need to wrap and found the position that overflows */
+   if (wrap > 0)
+     {
+        int index = wrap;
+        ch = GET_NEXT(str, index);
+        if (!_is_white(ch))
+          wrap = _layout_word_start(str, wrap);
+        /* If we found where to cut the text at, i.e the start
+         * of the word we were pointing at */
+        if (wrap > 0)
+          {
+             twrap = wrap;
+             ch = GET_PREV(str, twrap);
+             /* the text intersects the wrap point on a whitespace char */
+             if (_is_white(ch))
+               {
+                  return wrap;
+               }
+             /* intersects a word */
+             else
+               {
+                  /* walk back to start of word */
+                  twrap = _layout_word_start(str, wrap);
+                  if (twrap != 0)
+                    {
+                       wrap = twrap;
+                       ch = GET_PREV(str, wrap);
+                       return (str[wrap]) ? wrap : -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, index);
+
+             /* If there are already items in this line, we
+              * should just try creating a new line for it */
+             if (c->ln->items)
+               {
+                  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
+               {
+                  wrap = 0;
+                  twrap = _layout_word_end(ti->text, wrap);
+                  wrap = twrap;
+                  if (twrap >= 0)
+                    {
+                       ch = GET_NEXT(str, wrap);
+                       return (str[wrap]) ? wrap : -1;
+                    }
+                  else
+                    {
+                       return 0;
+                    }
+               }
+          }
+     }
+   /* 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)
+          {
+             return 0;
+          }
+        else
+          {
+             twrap = _layout_word_end(ti->text, wrap);
+             wrap = _layout_word_next(ti->text, wrap);
+             if (wrap >= 0)
+               return (str[wrap]) ? wrap : -1;
+             else if (twrap >= 0)
+               return (str[twrap]) ? wrap : -1;
+          }
+     }
+   return -1;
+}
+
 static void
 _layout_visualize_par(Ctxt *c)
 {
@@ -2881,29 +3074,97 @@ _layout_visualize_par(Ctxt *c)
      return;
 
    _layout_line_new(c, c->fmt);
-   EINA_LIST_FOREACH(c->par->logical_items, i, it)
-     {
-        c->ln->items = (Evas_Object_Textblock_Item *)
-           eina_inlist_append(EINA_INLIST_GET(c->ln->items),
-                 EINA_INLIST_GET(it));
-        /* FIXME: Add word wrappnig here */
-        if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
-          {
-             Evas_Object_Textblock_Format_Item *fi;
-             fi = _ITEM_FORMAT(it);
-             fi->y = c->y;
-             if (_IS_LINE_SEPARATOR(
-                      eina_strbuf_string_get(fi->source_node->format)))
+   /* We walk on our own because we want to be able to add items from
+    * inside the list and then walk them on the next iteration. */
+   for (i = c->par->logical_items ; i ; )
+     {
+        int adv_line = 0;
+        int redo_item = 0;
+        it = _ITEM(eina_list_data_get(i));
+        /* Skip visually deleted items */
+        if (it->visually_deleted)
+          {
+             i = eina_list_next(i);
+             continue;
+          }
+        /* FIXME: Add item merging back here */
+        /* Check if we need to wrap, i.e the text is bigger than the width
+         * Only calculate wrapping if the width of the object is > 0 */
+        if ((c->w >= 0) &&
+              ((c->fmt->wrap_word) || (c->fmt->wrap_char)) &&
+              ((c->x + it->adv) >
+               (c->w - c->o->style_pad.l - c->o->style_pad.r -
+                c->marginl - c->marginr)))
+          {
+             if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
                {
-                  _layout_line_advance(c, c->fmt, EINA_TRUE);
+                  /* Don't wrap if it's the only item */
+                  if (c->ln->items)
+                    {
+                       /*FIXME: I should handle tabs correctly, i.e like
+                        * spaces */
+                       _layout_line_advance(c, c->fmt, EINA_TRUE);
+                    }
+               }
+             else
+               {
+                  Evas_Object_Textblock_Text_Item *ti = _ITEM_TEXT(it);
+                  int wrap;
+
+                  adv_line = 1;
+                  if (c->fmt->wrap_word)
+                    {
+                       wrap = _layout_get_wordwrap(c, c->fmt, ti);
+                       if (wrap > 0)
+                         {
+                            _layout_item_text_split_strip_white(c, ti, wrap);
+                         }
+                       else if (c->ln->items)
+                         {
+                            /* Should wrap before the item */
+                            adv_line = 0;
+                            redo_item = 1;
+                            _layout_line_advance(c, c->fmt, EINA_TRUE);
+                         }
+                    }
+                  else if (c->fmt->wrap_char)
+                    {
+                       wrap = _layout_get_charwrap(c, c->fmt, ti);
+                       if (wrap > 0)
+                         {
+                            _layout_item_text_split_strip_white(c, ti, wrap);
+                         }
+                    }
                }
           }
-        else
+
+        if (!redo_item)
           {
-             c->fmt = _ITEM_TEXT(it)->format;
+             c->ln->items = (Evas_Object_Textblock_Item *)
+                eina_inlist_append(EINA_INLIST_GET(c->ln->items),
+                      EINA_INLIST_GET(it));
+             if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
+               {
+                  Evas_Object_Textblock_Format_Item *fi;
+                  fi = _ITEM_FORMAT(it);
+                  fi->y = c->y;
+                  if (_IS_LINE_SEPARATOR(fi->item))
+                    {
+                       adv_line = 1;
+                    }
+               }
+             else
+               {
+                  c->fmt = _ITEM_TEXT(it)->format;
+               }
+             c->x += it->adv;
+             i = eina_list_next(i);
           }
+        if (adv_line)
+          _layout_line_advance(c, c->fmt, EINA_TRUE);
      }
-   _layout_line_advance(c, c->fmt, EINA_FALSE);
+   if (c->ln->items)
+     _layout_line_advance(c, c->fmt, EINA_FALSE);
 }
 
 /**
@@ -2922,9 +3183,7 @@ _layout(const Evas_Object *obj, int calc_only, int w, int h, int *w_ret, int *h_
 {
    Evas_Object_Textblock *o;
    Ctxt ctxt, *c;
-   Evas_Object_Textblock_Line *ln;
    Evas_Object_Textblock_Node_Text *n;
-   Eina_List *removes = NULL;
    Evas_Object_Textblock_Format *fmt = NULL;
    int style_pad_l = 0, style_pad_r = 0, style_pad_t = 0, style_pad_b = 0;
 
@@ -2961,17 +3220,14 @@ _layout(const Evas_Object *obj, int calc_only, int w, int h, int *w_ret, int *h_
         if (h_ret) *h_ret = 0;
         return;
      }
-   /* run through all text and format nodes generating lines */
-   if (!c->o->text_nodes && !c->o->format_nodes)
+
+   /* If there are no nodes and lines, do the initial creation. */
+   if (!c->o->text_nodes)
      {
-        /* If there are no nodes and lines, do the initial creation. */
-        if (!c->par && !c->ln)
-          {
-             _layout_paragraph_new(c, NULL);
-             _layout_line_new(c, fmt);
-             _layout_text_append(c, fmt, NULL, 0, 0, NULL);
-             _layout_line_advance(c, fmt, EINA_TRUE);
-          }
+        _layout_paragraph_new(c, NULL);
+        _layout_line_new(c, fmt);
+        _layout_text_append(c, fmt, NULL, 0, 0, NULL);
+        _layout_line_advance(c, fmt, EINA_FALSE);
      }
 
    /* Go through all the text nodes to create the layout from */
@@ -3022,7 +3278,9 @@ _layout(const Evas_Object *obj, int calc_only, int w, int h, int *w_ret, int *h_
 
    /* FIXME: move away? */
    {
-      /* FIXME: is this the right format? or maybe it can change with pops? */
+      /* FIXME: is this the right format? or maybe it can change with pops?
+       * maybe this is the last? we need the first... Maybe we should
+       * just keep at the begining or something */
       c->fmt = c->format_stack->data;
       Evas_Object_Textblock_Paragraph *par;
       EINA_INLIST_FOREACH(c->paragraphs, par)
@@ -3033,10 +3291,6 @@ _layout(const Evas_Object *obj, int calc_only, int w, int h, int *w_ret, int *h_
         }
    }
 
-   /* Advance the line so it'll calculate the size */
-   if ((c->ln) && (c->ln->items) && (fmt))
-     //_layout_line_advance(c, fmt, EINA_TRUE);
-
    /* Clean the rest of the format stack */
    while (c->format_stack)
      {
@@ -3044,25 +3298,13 @@ _layout(const Evas_Object *obj, int calc_only, int w, int h, int *w_ret, int *h_
         c->format_stack = eina_list_remove_list(c->format_stack, c->format_stack);
         _format_unref_free(c->obj, fmt);
      }
-   EINA_INLIST_FOREACH(c->par->lines, ln)
-     {
-        if (ln->line_no == -1)
-          {
-             removes = eina_list_append(removes, ln);
-          }
-        else
-          {
-             if ((ln->par->y + ln->y + ln->h) > c->hmax) c->hmax = ln->par->y + ln->y + ln->h;
-          }
-     }
-   /* Remove the lines we marked for removal, mostly lines added after
-    * just to force calculation of line sizes */
-   while (removes)
+
+   if (c->paragraphs)
      {
-        ln = removes->data;
-        c->par->lines = (Evas_Object_Textblock_Line *)eina_inlist_remove(EINA_INLIST_GET(c->par->lines), EINA_INLIST_GET(ln));
-        removes = eina_list_remove_list(removes, removes);
-        _line_free(obj, ln);
+        Evas_Object_Textblock_Paragraph *last_par;
+        last_par = (Evas_Object_Textblock_Paragraph *)
+           EINA_INLIST_GET(c->paragraphs)->last;
+        c->hmax = last_par->y + last_par->h;
      }
 
    if (w_ret) *w_ret = c->wmax;
@@ -8177,6 +8419,57 @@ ptnode(Evas_Object_Textblock_Node_Text *n)
    printf("next = %p, prev = %p, last = %p\n", EINA_INLIST_GET(n)->next, EINA_INLIST_GET(n)->prev, EINA_INLIST_GET(n)->last);
    printf("format_node = %p\n", n->format_node);
    printf("'%ls'\n", eina_ustrbuf_string_get(n->unicode));
+   if (n->bidi_props)
+     {
+        size_t i;
+        for (i = 0 ; i < eina_ustrbuf_length_get(n->unicode) ; i++)
+          {
+             printf("%d", n->bidi_props->embedding_levels[i]);
+          }
+        printf("\n");
+     }
+}
+
+void
+pitem(Evas_Object_Textblock_Item *it)
+{
+   Evas_Object_Textblock_Text_Item *ti;
+   Evas_Object_Textblock_Format_Item *fi;
+   printf("Item: %p\n", it);
+   printf("Type: %s (%d)\n", (it->type == EVAS_TEXTBLOCK_ITEM_TEXT) ?
+         "TEXT" : "FORMAT", it->type);
+   printf("Text pos: %d Visual pos: %d\n", it->text_pos,
+#ifdef BIDI_SUPPORT
+         it->visual_pos
+#else
+         it->text_pos
+#endif
+         );
+   printf("Coords: x = %d w = %d adv = %d\n", (int) it->x, (int) it->w,
+         (int) it->adv);
+   if (it->type == EVAS_TEXTBLOCK_ITEM_TEXT)
+     {
+        ti = _ITEM_TEXT(it);
+        printf("Text: '%ls'\n", ti->text);
+     }
+   else
+     {
+        fi = _ITEM_FORMAT(it);
+        printf("Format: '%s'\n", fi->item);
+     }
 }
+
+void
+ppar(Evas_Object_Textblock_Paragraph *par)
+{
+   Evas_Object_Textblock_Item *it;
+   Eina_List *i;
+   EINA_LIST_FOREACH(par->logical_items, i, it)
+     {
+        printf("***********************\n");
+        pitem(it);
+     }
+}
+
 #endif
 
index 15bfcb1..38a2dc8 100644 (file)
@@ -134,6 +134,61 @@ evas_common_font_ot_cutoff_text_props(Evas_Text_Props *props, int cutoff)
 
 }
 
+/* Won't work in the middle of ligatures
+ * assumes ext doesn't have an already init ot_data
+ * we assume there's at least one char in each part */
+EAPI void
+evas_common_font_ot_split_text_props(Evas_Text_Props *base,
+      Evas_Text_Props *ext, int cutoff)
+{
+   Evas_Font_OT_Data_Item *tmp;
+   int i;
+   if ((cutoff <= 0) || (!base->ot_data) ||
+         (((size_t) cutoff) >= base->ot_data->len))
+     return;
+
+   ext->ot_data = calloc(1, sizeof(Evas_Font_OT_Data));
+   ext->ot_data->refcount = 1;
+   ext->ot_data->len = base->ot_data->len - cutoff;
+   ext->ot_data->items = calloc(ext->ot_data->len,
+         sizeof(Evas_Font_OT_Data_Item));
+   if (base->bidi.dir == EVAS_BIDI_DIRECTION_RTL)
+     {
+        memcpy(ext->ot_data->items, base->ot_data->items,
+              ext->ot_data->len * sizeof(Evas_Font_OT_Data_Item));
+        memmove(base->ot_data->items,
+              base->ot_data->items + ext->ot_data->len,
+              cutoff * sizeof(Evas_Font_OT_Data_Item));
+     }
+   else
+     {
+        memcpy(ext->ot_data->items, base->ot_data->items + cutoff,
+              ext->ot_data->len * sizeof(Evas_Font_OT_Data_Item));
+     }
+
+   /* Adjust the offset of the clusters */
+     {
+        size_t min;
+        min = ext->ot_data->items[0].source_cluster;
+        for (i = 1 ; i < (int) ext->ot_data->len ; i++)
+          {
+             if (ext->ot_data->items[i].source_cluster < min)
+               {
+                  min = ext->ot_data->items[i].source_cluster;
+               }
+          }
+        for (i = 0 ; i < (int) ext->ot_data->len ; i++)
+          {
+             ext->ot_data->items[i].source_cluster -= min;
+          }
+     }
+   tmp = realloc(base->ot_data->items,
+         cutoff * sizeof(Evas_Font_OT_Data_Item));
+   base->ot_data->items = tmp;
+   base->ot_data->len = cutoff;
+
+}
+
 EAPI Eina_Bool
 evas_common_font_ot_populate_text_props(void *_fn, const Eina_Unicode *text,
       Evas_Text_Props *props, int len)
index 5ec19ce..ab4a146 100644 (file)
@@ -78,5 +78,8 @@ evas_common_font_ot_populate_text_props(void *fn, const Eina_Unicode *text,
 
 EAPI void
 evas_common_font_ot_cutoff_text_props(Evas_Text_Props *props, int cutoff);
+
+EAPI void
+evas_common_font_ot_split_text_props(Evas_Text_Props *base, Evas_Text_Props *ext, int cutoff);
 #endif
 
index fbe4dd0..c6250e2 100644 (file)
@@ -666,7 +666,7 @@ evas_common_font_query_char_at_coords(RGBA_Font *fn, const Eina_Unicode *in_text
 
 end:
 
-  evas_common_font_int_use_trim();
+   evas_common_font_int_use_trim();
    return ret_val;
 }
 
index 50ca45e..e9eee31 100644 (file)
@@ -66,4 +66,17 @@ evas_common_text_props_cutoff(Evas_Text_Props *props, int cutoff)
 #endif
 }
 
+/* Won't work in the middle of ligatures */
+EAPI void
+evas_common_text_props_split(Evas_Text_Props *base,
+      Evas_Text_Props *ext, int cutoff)
+{
+   /* FIXME: move to their own functions */
+   memcpy(&ext->bidi, &base->bidi, sizeof(Evas_BiDi_Props));
+   memcpy(&ext->script, &base->script, sizeof(Evas_Script_Type));
+#ifdef OT_SUPPORT
+   evas_common_font_ot_split_text_props(base, ext, cutoff);
+#endif
+}
+
 
index ecc6a93..6a36a19 100644 (file)
@@ -32,4 +32,7 @@ evas_common_text_props_content_unref(Evas_Text_Props *props);
 EAPI void
 evas_common_text_props_cutoff(Evas_Text_Props *props, int cutoff);
 
+EAPI void
+evas_common_text_props_split(Evas_Text_Props *base, Evas_Text_Props *ext,
+      int cutoff);
 #endif