tasn pushed a commit to branch master.

http://git.enlightenment.org/core/efl.git/commit/?id=e8afd5241cb0fc99a6f6d2ba0e83d8987507a03a

commit e8afd5241cb0fc99a6f6d2ba0e83d8987507a03a
Author: Daniel Hirt <daniel.h...@samsung.com>
Date:   Wed Jul 1 16:24:31 2015 +0100

    Evas Textblock: Add obstacle objects feature
    
    Summary:
    Introducing a new feature for Evas Textblock. This allows the layout to
    wrap around other evas objects.
    The following API is added:
     - obstacle_add
     - obstacle_del
     - obstacle_update
    Evas objects can now serve as textblock obstacles, if positioned and
    visible on the text area. The text will wrap around the obstacles
    according to the wrapping mode set to it.
    
    This also modifies the current wrapping code to handle obstacle wrap
    points as well. The wrap index query function is modified so that
    forward-scanning (specific cases) may be disabled when treating
    obstacle wrap point.
    
    RTL text is currently unsupported by this feature.
    Consult added docs and example for usage.
    
    @feature
    
    Test Plan: Evas example and test in evas_suite are provided with this.
    
    Reviewers: tasn
    
    Subscribers: raster, JackDanielZ, cedric
    
    Differential Revision: https://phab.enlightenment.org/D2405
---
 src/examples/evas/Makefile.am                |   5 +
 src/examples/evas/Makefile.examples          |   3 +-
 src/examples/evas/evas-textblock-obstacles.c | 311 +++++++++++++++++++++++++
 src/lib/evas/canvas/evas_object_textblock.c  | 328 +++++++++++++++++++++++++--
 src/lib/evas/canvas/evas_textblock.eo        |  40 ++++
 src/tests/evas/evas_test_textblock.c         |  88 +++++++
 6 files changed, 755 insertions(+), 20 deletions(-)

diff --git a/src/examples/evas/Makefile.am b/src/examples/evas/Makefile.am
index 5c39cbd..d271652 100644
--- a/src/examples/evas/Makefile.am
+++ b/src/examples/evas/Makefile.am
@@ -159,6 +159,11 @@ evas_text_SOURCES = evas-text.c
 evas_text_LDADD = $(ECORE_EVAS_COMMON_LDADD)
 evas_text_CPPFLAGS = $(ECORE_EVAS_COMMON_CPPFLAGS)
 
+EXTRA_PROGRAMS += evas_textblock_obstacles
+evas_textblock_obstacles_SOURCES = evas-textblock-obstacles.c
+evas_textblock_obstacles_LDADD = $(ECORE_EVAS_COMMON_LDADD)
+evas_textblock_obstacles_CPPFLAGS = $(ECORE_EVAS_COMMON_CPPFLAGS)
+
 EXTRA_PROGRAMS += evas_smart_object
 evas_smart_object_SOURCES = evas-smart-object.c
 evas_smart_object_LDADD = $(ECORE_EVAS_COMMON_LDADD)
diff --git a/src/examples/evas/Makefile.examples 
b/src/examples/evas/Makefile.examples
index 313be1b..0d2b33a 100644
--- a/src/examples/evas/Makefile.examples
+++ b/src/examples/evas/Makefile.examples
@@ -23,7 +23,8 @@ EXAMPLES= evas-aspect-hints \
           evas-smart-object \
           evas-stacking \
           evas-table \
-          evas-text
+          evas-text \
+          evas-textblock-obstacles
 
 all: edje examples
 edje: $(EDJE_OBJS)
diff --git a/src/examples/evas/evas-textblock-obstacles.c 
b/src/examples/evas/evas-textblock-obstacles.c
new file mode 100644
index 0000000..babcfae
--- /dev/null
+++ b/src/examples/evas/evas-textblock-obstacles.c
@@ -0,0 +1,311 @@
+/**
+ * Evas textblock example for obstacles feature
+ *
+ * You'll need at least one engine built for it (excluding the buffer
+ * one). See stdout/stderr for output.
+ *
+ * You start with two registered obstacle objects. They are not visible
+ * at first, so the textblock simply shows the text that has been set to it.
+ * Once the obstacle is visible (show/hide keys in the example), the text will
+ * wrap around it.
+ * This example allows you to test two obstacles registered to the same
+ * textblock object. Also, you can play with size and position for each.
+ * Use the 'h' key to show the provided options for this test.
+ *
+ * @verbatim
+ * gcc -o evas-textblock-obstacles evas-textblock-obstacles.c `pkg-config 
--libs --cflags evas ecore ecore-evas`
+ * @endverbatim
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#else
+#define PACKAGE_EXAMPLES_DIR "."
+#endif
+
+#include <Ecore.h>
+#include <Ecore_Evas.h>
+#include <stdio.h>
+#include <errno.h>
+#include "evas-common.h"
+
+#define WIDTH  (320)
+#define HEIGHT (240)
+
+#define POINTER_CYCLE(_ptr, _array)                             \
+  do                                                            \
+    {                                                           \
+       if ((unsigned int)(((unsigned char *)(_ptr)) - ((unsigned char 
*)(_array))) >= \
+           sizeof(_array))                                      \
+         _ptr = _array;                                         \
+    }                                                           \
+  while(0)
+
+static const char *commands = \
+  "commands are:\n"
+  "\tt - change currently controlled obstacle\n"
+  "\tv - show/hide current obstacle\n"
+  "\ts - cycle current obstacle's size\n"
+  "\tp - change current obstacle's position (random)\n"
+  "\tw - cycle text wrapping modes (none/word/char/mixed)\n"
+  "\th - print help\n";
+
+struct text_preset_data
+{
+   const char        **font_ptr;
+   const char         *font[3];
+
+   const char        **wrap_ptr;
+   const char         *wrap[4];
+
+   Evas_Coord         *obs_size_ptr;
+   Evas_Coord          obs_size[3];
+
+   Evas_Object **obs_ptr; /* pointer to the currently controlled obstacle 
object */
+   Evas_Object *obs[2];
+};
+
+struct test_data
+{
+   Ecore_Evas             *ee;
+   Evas                   *evas;
+   struct text_preset_data t_data;
+   Evas_Object            *text, *bg;
+   Evas_Coord             w, h;
+   Evas_Textblock_Style   *st;
+};
+
+static struct test_data d = {0};
+
+static void
+_on_destroy(Ecore_Evas *ee EINA_UNUSED)
+{
+   ecore_main_loop_quit();
+}
+
+static void
+_canvas_resize_cb(Ecore_Evas *ee)
+{
+   int w, h;
+
+   ecore_evas_geometry_get(ee, NULL, NULL, &w, &h);
+   evas_object_resize(d.bg, w, h);
+   evas_object_resize(d.text, w, h);
+   d.w = w;
+   d.h = h;
+}
+
+static unsigned int
+_getrand(unsigned int low, unsigned int high)
+{
+   return (rand() % (high - low)) + low;
+}
+
+static void
+_style_set(const char *wrap)
+{
+   char buf[2000];
+   snprintf(buf,
+         2000,
+         "DEFAULT='font=Sans font_size=16 color=#000 wrap=%s text_class=entry'"
+         "br='\n'"
+         "ps='ps'"
+         "tab='\t'",
+         wrap);
+   evas_textblock_style_set(d.st, buf);
+}
+
+static void
+_on_keydown(void        *data EINA_UNUSED,
+            Evas        *evas EINA_UNUSED,
+            Evas_Object *o EINA_UNUSED,
+            void        *einfo)
+{
+   Evas_Event_Key_Down *ev = einfo;
+
+   if (strcmp(ev->key, "h") == 0) /* print help */
+     {
+        fprintf(stdout, commands);
+        return;
+     }
+
+   if (strcmp(ev->key, "t") == 0) /* change obstacle type */
+     {
+        (d.t_data.obs_ptr)++;
+        POINTER_CYCLE(d.t_data.obs_ptr, d.t_data.obs);
+
+        fprintf(stdout, "Now controlling obstacle: %p\n", *d.t_data.obs_ptr);
+
+        return;
+     }
+
+   if (strcmp(ev->key, "v") == 0) /* change obstacle visibility */
+     {
+        Eo *obj = *d.t_data.obs_ptr;
+        if (evas_object_visible_get(obj))
+           evas_object_hide(obj);
+        else
+           evas_object_show(obj);
+        fprintf(stdout, "Show/hide toggle for obstacle %p\n",
+              *d.t_data.obs_ptr);
+        evas_object_textblock_obstacles_update(d.text);
+
+        return;
+     }
+
+   if (strcmp(ev->key, "s") == 0) /* change obstacle size */
+     {
+        (d.t_data.obs_size_ptr)++;
+        POINTER_CYCLE(d.t_data.obs_size_ptr, d.t_data.obs_size);
+
+        evas_object_resize(*d.t_data.obs_ptr,
+              *d.t_data.obs_size_ptr,
+              *d.t_data.obs_size_ptr);
+
+        evas_object_textblock_obstacles_update(d.text);
+
+        fprintf(stdout, "Changing obstacle size to: %d,%d\n", 
*d.t_data.obs_size_ptr, *d.t_data.obs_size_ptr);
+
+        return;
+     }
+   if (strcmp(ev->key, "p") == 0) /* change obstacle position */
+     {
+        Evas_Coord x, y;
+        Evas_Coord rx, ry, gx, gy;
+        x = _getrand(0, d.w);
+        y = _getrand(0, d.h);
+        evas_object_move(*d.t_data.obs_ptr, x, y);
+        evas_object_textblock_obstacles_update(d.text);
+
+        fprintf(stdout, "Changing obstacles position\n");
+        evas_object_move(*d.t_data.obs_ptr, x, y);
+        evas_object_geometry_get(d.t_data.obs[0], &rx, &ry, NULL, NULL);
+        evas_object_geometry_get(d.t_data.obs[1], &gx, &gy, NULL, NULL);
+        fprintf(stdout, "Obstacle #1 (red)  : [%d,%d]\n", rx, ry);
+        fprintf(stdout, "Obstacle #2 (green): [%d,%d]\n", gx, gy);
+
+        return;
+     }
+   if (strcmp(ev->key, "w") == 0) /* change obstacle position */
+     {
+        (d.t_data.wrap_ptr)++;
+        POINTER_CYCLE(d.t_data.wrap_ptr, d.t_data.wrap);
+        fprintf(stdout, "Changing wrap mode to: %s\n", *d.t_data.wrap_ptr);
+        _style_set(*d.t_data.wrap_ptr);
+        evas_object_textblock_obstacles_update(d.text);
+
+        return;
+     }
+}
+
+static void
+_obs_init(Evas_Object *obj)
+{
+   evas_object_resize(obj, 50, 50);
+}
+
+static void
+_text_init()
+{
+   d.st = evas_textblock_style_new();
+   evas_object_textblock_style_set(d.text, d.st);
+   _style_set("word");
+
+   evas_object_textblock_text_markup_set(d.text,
+         "This is an example text to demonstrate the textblock object"
+         " with obstacle objects support."
+         " Any evas object <item size=72x16></item>can register itself as an 
obstacle to the textblock"
+         " object. Upon reg<color=#0ff>stering, it aff</color>ects the layout 
of the text in"
+         " certain situations. Usually, when the obstacle shows above the text"
+         " area, it will cause the layout of the text to split and move"
+         " parts of it, so that all text area is apparent."
+         );
+}
+
+int
+main(void)
+{
+   if (!ecore_evas_init())
+     return EXIT_FAILURE;
+
+   /* example obstacles types */
+   Evas_Object *rect, *rect2;
+
+   /* init values one is going to cycle through while running this
+    * example */
+   struct text_preset_data init_data =
+   {
+      .font = {"DejaVu", "Courier", "Utopia"},
+      .wrap = {"word", "char", "mixed", "none"},
+      .obs_size = {50, 70, 100},
+      .obs = {NULL, NULL},
+   };
+
+   d.t_data = init_data;
+   d.t_data.font_ptr = d.t_data.font;
+   d.t_data.obs_size_ptr = d.t_data.obs_size;
+   d.t_data.obs_ptr = d.t_data.obs;
+
+   /* this will give you a window with an Evas canvas under the first
+    * engine available */
+   d.ee = ecore_evas_new(NULL, 0, 0, WIDTH, HEIGHT, NULL);
+   if (!d.ee)
+     goto error;
+   printf("Window size set to [%d,%d]\n", WIDTH, HEIGHT);
+
+   ecore_evas_callback_delete_request_set(d.ee, _on_destroy);
+   ecore_evas_callback_resize_set(d.ee, _canvas_resize_cb);
+   ecore_evas_show(d.ee);
+
+   d.evas = ecore_evas_get(d.ee);
+
+   d.bg = evas_object_rectangle_add(d.evas);
+   evas_object_color_set(d.bg, 255, 255, 255, 255); /* white bg */
+   evas_object_move(d.bg, 0, 0); /* at canvas' origin */
+   evas_object_resize(d.bg, WIDTH, HEIGHT); /* covers full canvas */
+   evas_object_show(d.bg);
+
+   evas_object_focus_set(d.bg, EINA_TRUE);
+   evas_object_event_callback_add(
+     d.bg, EVAS_CALLBACK_KEY_DOWN, _on_keydown, NULL);
+
+   d.text = evas_object_textblock_add(d.evas);
+   _text_init();
+   evas_object_resize(d.text, WIDTH, HEIGHT);
+   evas_object_move(d.text, 0, 0);
+   evas_object_show(d.text);
+   d.w = WIDTH;
+   d.h = HEIGHT;
+
+   /* init obstacles */
+   rect = evas_object_rectangle_add(d.evas);
+   d.t_data.obs[0] = rect;
+   evas_object_color_set(rect, 255, 0, 0, 255);
+   _obs_init(rect);
+   rect2 = evas_object_rectangle_add(d.evas);
+   d.t_data.obs[1] = rect2;
+   evas_object_color_set(rect2, 0, 255, 0, 255);
+   _obs_init(rect2);
+
+   evas_object_textblock_obstacle_add(d.text, rect);
+   evas_object_textblock_obstacle_add(d.text, rect2);
+
+   evas_object_show(d.t_data.obs[0]);
+   evas_object_show(d.t_data.obs[1]);
+
+   fprintf(stdout, commands);
+   ecore_main_loop_begin();
+
+   evas_textblock_style_free(d.st);
+   ecore_evas_free(d.ee);
+   ecore_evas_shutdown();
+
+   return 0;
+
+error:
+   fprintf(stderr, "you got to have at least one evas engine built and linked"
+                   " up to ecore-evas for this example to run properly.\n");
+   ecore_evas_shutdown();
+   return -1;
+}
+
diff --git a/src/lib/evas/canvas/evas_object_textblock.c 
b/src/lib/evas/canvas/evas_object_textblock.c
index 6351b1f..4e8ffaa 100644
--- a/src/lib/evas/canvas/evas_object_textblock.c
+++ b/src/lib/evas/canvas/evas_object_textblock.c
@@ -497,6 +497,7 @@ struct _Evas_Object_Textblock
    Eina_List                          *ellip_prev_it; /* item that is placed 
before ellipsis item (0.0 <= ellipsis < 1.0), if required */
    Eina_List                          *anchors_a;
    Eina_List                          *anchors_item;
+   Eina_List                          *obstacles;
    int                                 last_w, last_h;
    struct {
       int                              l, r, t, b;
@@ -512,6 +513,7 @@ struct _Evas_Object_Textblock
    } formatted, native;
    Eina_Bool                           redraw : 1;
    Eina_Bool                           changed : 1;
+   Eina_Bool                           obstacle_changed : 1;
    Eina_Bool                           content_changed : 1;
    Eina_Bool                           format_changed : 1;
    Eina_Bool                           have_ellipsis : 1;
@@ -2560,6 +2562,8 @@ struct _Ctxt
    Eina_List *format_stack;
    Evas_Object_Textblock_Format *fmt;
 
+   Eina_List *obs_infos; /**< Extra information for items in current line. */
+
    int x, y;
    int w, h;
    int wmax, hmax;
@@ -3405,6 +3409,12 @@ _layout_last_line_max_descent_adjust_calc(Ctxt *c, const 
Evas_Object_Textblock_P
 
    return 0;
 }
+typedef struct _Evas_Textblock_Obstacle_Info
+{
+   Evas_Object_Textblock_Item *it; /**< the corresponding item node. */
+   Evas_Coord obs_adv;
+   Evas_Coord obs_preadv;
+} Evas_Textblock_Obstacle_Info;
 
 /**
  * @internal
@@ -3419,6 +3429,9 @@ static void
 _layout_line_finalize(Ctxt *c, Evas_Object_Textblock_Format *fmt)
 {
    Evas_Object_Textblock_Item *it;
+   Evas_Coord obs_preadv = 0, obs_adv = 0;
+   Eina_List *i;
+   Evas_Textblock_Obstacle_Info *obs_info = NULL;
    Evas_Coord x = 0;
 
    /* If there are no text items yet, calc ascent/descent
@@ -3463,12 +3476,28 @@ _layout_line_finalize(Ctxt *c, 
Evas_Object_Textblock_Format *fmt)
           }
 
 loop_advance:
+        obs_preadv = 0;
+        obs_adv = 0;
+        EINA_LIST_FOREACH(c->obs_infos, i, obs_info)
+          {
+             if (obs_info->it == it)
+               {
+                  obs_preadv += obs_info->obs_preadv;
+                  obs_adv += obs_info->obs_adv;
+               }
+          }
+        x += obs_preadv;
         it->x = x;
-        x += it->adv;
+        x += it->adv + obs_adv;
 
         if ((it->w > 0) && ((it->x + it->w) > c->ln->w)) c->ln->w = it->x + 
it->w;
      }
 
+   /* clear obstacle info for this line */
+   EINA_LIST_FREE(c->obs_infos, obs_info)
+     {
+        free(obs_info);
+     }
    c->ln->y = c->y - c->par->y;
    c->ln->h = c->ascent + c->descent;
 
@@ -4315,7 +4344,7 @@ _layout_get_charwrap(Ctxt *c, 
Evas_Object_Textblock_Format *fmt,
 static int
 _layout_get_word_mixwrap_common(Ctxt *c, Evas_Object_Textblock_Format *fmt,
       const Evas_Object_Textblock_Item *it, Eina_Bool mixed_wrap,
-      size_t line_start, const char *breaks)
+      size_t line_start, const char *breaks, Eina_Bool scan_fwd)
 {
    Eina_Bool wrap_after = EINA_FALSE;
    size_t wrap;
@@ -4371,7 +4400,7 @@ _layout_get_word_mixwrap_common(Ctxt *c, 
Evas_Object_Textblock_Format *fmt,
                   return ((orig_wrap >= line_start) && (orig_wrap < len)) ?
                      ((int) orig_wrap) : -1;
                }
-             else
+             else if (scan_fwd)
                {
                   /* Scan forward to find the next wrapping point */
                   wrap = orig_wrap;
@@ -4383,6 +4412,7 @@ _layout_get_word_mixwrap_common(Ctxt *c, 
Evas_Object_Textblock_Format *fmt,
    /* If we need to find the position after the cutting point */
    if ((wrap == line_start) || (wrap_after))
      {
+        if (!scan_fwd) return wrap;
         if (mixed_wrap)
           {
              return _layout_get_charwrap(c, fmt, it,
@@ -4417,20 +4447,20 @@ _layout_get_word_mixwrap_common(Ctxt *c, 
Evas_Object_Textblock_Format *fmt,
 static int
 _layout_get_wordwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
       const Evas_Object_Textblock_Item *it, size_t line_start,
-      const char *breaks)
+      const char *breaks, Eina_Bool allow_scan_fwd)
 {
    return _layout_get_word_mixwrap_common(c, fmt, it, EINA_FALSE, line_start,
-         breaks);
+         breaks, allow_scan_fwd);
 }
 
 /* -1 means no wrap */
 static int
 _layout_get_mixedwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
       const Evas_Object_Textblock_Item *it, size_t line_start,
-      const char *breaks)
+      const char *breaks, Eina_Bool allow_scan_fwd)
 {
    return _layout_get_word_mixwrap_common(c, fmt, it, EINA_TRUE, line_start,
-         breaks);
+         breaks, allow_scan_fwd);
 }
 
 static Evas_Object_Textblock_Text_Item *
@@ -4737,6 +4767,20 @@ _layout_par_append_ellipsis(Ctxt *c)
    ellip_ti->parent.ln = c->ln;
    c->x += ellip_ti->parent.adv;
 }
+/* obstacles */
+static inline void
+_layout_obstacles_update(Ctxt *c);
+
+typedef struct _Evas_Textblock_Obstacle
+{
+   Eo *eo_obs; /**< Pointer to evas object which serves as an obstacle. */
+   Evas_Coord x, y, w, h; /**< Geometry of the obstacle object. x,y are
+   the offset position of the obstacle relative to the textblock object. */
+   Eina_Bool visible : 1;
+} Evas_Textblock_Obstacle;
+
+static Evas_Textblock_Obstacle *
+_layout_item_obstacle_get(Ctxt *c, Evas_Object_Textblock_Item *it);
 
 /* 0 means go ahead, 1 means break without an error, 2 means
  * break with an error, should probably clean this a bit (enum/macro)
@@ -4750,6 +4794,9 @@ _layout_par(Ctxt *c)
    int wrap = -1;
    char *line_breaks = NULL;
 
+   /* Obstacles logic */
+   Eina_Bool handle_obstacles = EINA_FALSE;
+
    if (!c->par->logical_items)
      return 2;
 
@@ -4766,7 +4813,7 @@ _layout_par(Ctxt *c)
          * and we aren't just calculating. */
         if (!c->par->text_node->is_new && !c->par->text_node->dirty &&
               !c->width_changed && c->par->lines &&
-              !c->o->have_ellipsis)
+              !c->o->have_ellipsis && !c->o->obstacle_changed)
           {
              Evas_Object_Textblock_Line *ln;
              /* Update c->line_no */
@@ -4782,6 +4829,14 @@ _layout_par(Ctxt *c)
 
              return 0;
           }
+
+        /* Update all obstacles */
+        if (c->o->obstacle_changed || c->width_changed)
+          {
+             _layout_obstacles_update(c);
+             handle_obstacles = EINA_TRUE;
+          }
+
         c->par->text_node->dirty = EINA_FALSE;
         c->par->text_node->is_new = EINA_FALSE;
         c->par->rendered = EINA_FALSE;
@@ -4837,11 +4892,15 @@ _layout_par(Ctxt *c)
            _layout_par_ellipsis_items(c, ellip);
      }
 
+   Eina_Bool item_preadv = EINA_FALSE;
+   Evas_Textblock_Obstacle *obs = NULL;
    for (i = c->par->logical_items ; i ; )
      {
         Evas_Coord prevdescent = 0, prevascent = 0;
         int adv_line = 0;
         int redo_item = 0;
+        Evas_Textblock_Obstacle_Info *obs_info = NULL;
+
         it = _ITEM(eina_list_data_get(i));
         /* Skip visually deleted items */
         if (it->visually_deleted)
@@ -4879,13 +4938,17 @@ _layout_par(Ctxt *c)
                }
           }
 
-
+        if (handle_obstacles && !obs)
+          {
+             obs = _layout_item_obstacle_get(c, it);
+          }
         /* Check if we need to wrap, i.e the text is bigger than the width,
            or we already found a wrap point. */
         if ((c->w >= 0) &&
-              (((c->x + it->w) >
-                (c->w - c->o->style_pad.l - c->o->style_pad.r -
-                 c->marginl - c->marginr)) || (wrap > 0)))
+              (obs ||
+                 (((c->x + it->w) >
+                   (c->w - c->o->style_pad.l - c->o->style_pad.r -
+                    c->marginl - c->marginr)) || (wrap > 0))))
           {
              /* Handle ellipsis here. If we don't have more width left
               * and no height left, or no more width left and no wrapping.
@@ -4940,21 +5003,42 @@ _layout_par(Ctxt *c)
                   else
                      line_start = it->text_pos;
 
-                  adv_line = 1;
+                  /* Only when doing non-obstacle handling */
+                  if (!obs)
+                     adv_line = 1;
                   /* If we don't already have a wrap point from before */
                   if (wrap < 0)
                     {
+                       /* Originally with wrapping, we may have ended up
+                        * wrapping on the item next to the current one,
+                        * if the current one was the first item in a line.
+                        * This is different with obstacles: we allow the
+                        * wrapping point algorithm to consider the first
+                        * item in a line as well, and "push" it forward
+                        * after the obstacle.
+                        * There is one specific case with obstacles, where
+                        * we DON'T allow to scan forward on the textblock's
+                        * edges, and that's if the first item in a line
+                        * was pushed forward by an obstacle once, as there
+                        * is a chance it will fit in the next lines. */
+                       Eina_Bool allow_scan_fwd = (!obs && !item_preadv);
+                       Evas_Coord save_cw = c->w;
+                       if (obs)
+                         {
+                            c->w = obs->x;
+                         }
                        if (it->format->wrap_word)
                           wrap = _layout_get_wordwrap(c, it->format, it,
-                                line_start, line_breaks);
+                                line_start, line_breaks, allow_scan_fwd);
                        else if (it->format->wrap_char)
                           wrap = _layout_get_charwrap(c, it->format, it,
                                 line_start, line_breaks);
                        else if (it->format->wrap_mixed)
                           wrap = _layout_get_mixedwrap(c, it->format, it,
-                                line_start, line_breaks);
+                                line_start, line_breaks, allow_scan_fwd);
                        else
                           wrap = -1;
+                       c->w = save_cw;
                     }
 
                   /* If it's before the item, rollback and apply.
@@ -5013,6 +5097,13 @@ _layout_par(Ctxt *c)
                             wrap -= it->text_pos; /* Cut here */
                          }
                     }
+                  /* Specific case for obstacles */
+                  if (obs && (wrap >= 0))
+                    {
+                       obs_info = calloc(1, 
sizeof(Evas_Textblock_Obstacle_Info));
+                       obs_info->it = it;
+                       c->obs_infos = eina_list_append(c->obs_infos, obs_info);
+                    }
                   if ((wrap >= 0) && ((size_t) wrap == it_len))
                     {
                        /* Can happen if this is the last word in the paragraph 
*/
@@ -5025,6 +5116,11 @@ _layout_par(Ctxt *c)
                             _layout_item_text_split_strip_white(c,
                                   _ITEM_TEXT(it), i, wrap);
                          }
+                       if (obs)
+                         {
+                            obs_info->obs_adv = obs->x + obs->w - c->x - 
it->adv;
+                            c->x = obs->x + obs->w;
+                         }
                     }
                   else if (wrap == 0)
                     {
@@ -5037,7 +5133,17 @@ _layout_par(Ctxt *c)
 
                        adv_line = 0;
                        redo_item = 1;
-                       _layout_line_advance(c, it->format);
+                       if (obs)
+                         {
+                            obs_info->obs_preadv = obs->x + obs->w - c->x;
+                            c->x = obs->x + obs->w;
+                            item_preadv = EINA_TRUE;
+                         }
+                       else
+                         {
+                            _layout_line_advance(c, it->format);
+                            item_preadv = EINA_FALSE;
+                         }
                     }
                   else // (wrap < 0)
                     {
@@ -5045,6 +5151,7 @@ _layout_par(Ctxt *c)
                        adv_line = 0;
                     }
                   /* Reset wrap */
+                  obs = NULL;
                   wrap = -1;
                }
           }
@@ -5070,10 +5177,11 @@ _layout_par(Ctxt *c)
                        adv_line = 1;
                     }
                }
-             c->x += it->adv;
+             if (!obs_info) c->x += it->adv;
              if (c->o->ellip_prev_it == i)
                 _layout_par_append_ellipsis(c);
              i = eina_list_next(i);
+             item_preadv = EINA_FALSE;
           }
         if (adv_line)
           {
@@ -5509,6 +5617,7 @@ _layout(const Evas_Object *eo_obj, int w, int h, int 
*w_ret, int *h_ret)
    c->align_auto = EINA_TRUE;
    c->ln = NULL;
    c->width_changed = (obj->cur->geometry.w != o->last_w);
+   c->obs_infos = NULL;
 
    /* Start of logical layout creation */
    /* setup default base style */
@@ -5655,6 +5764,8 @@ _layout(const Evas_Object *eo_obj, int w, int h, int 
*w_ret, int *h_ret)
         LYDBG("ZZ: ... layout #2\n");
         _layout(eo_obj, w, h, w_ret, h_ret);
      }
+
+   c->o->obstacle_changed = EINA_FALSE;
 }
 
 /*
@@ -6879,6 +6990,182 @@ evas_textblock_text_utf8_to_markup(const Evas_Object 
*eo_obj, const char *text)
 
 }
 
+static void
+_obstacle_update(Evas_Textblock_Obstacle *obs, Eo *eo_obj)
+{
+   Evas_Coord x, y;
+   Evas_Coord ox, oy, ow, oh;
+   Eo *eo_obs = obs->eo_obs;
+
+   eo_do(eo_obs, efl_gfx_position_get(&ox, &oy), efl_gfx_size_get(&ow, &oh));
+   eo_do(eo_obj, efl_gfx_position_get(&x, &y));
+
+   obs->x = ox - x;
+   obs->y = oy - y;
+   obs->w = ow;
+   obs->h = oh;
+}
+
+static void
+_layout_obstacles_update(Ctxt *c)
+{
+   Eina_List *i;
+   Eina_Bool obstacle_changed = c->o->obstacle_changed;
+   Evas_Textblock_Obstacle *obs;
+
+   EINA_LIST_FOREACH(c->o->obstacles, i, obs)
+     {
+        if (obstacle_changed)
+           _obstacle_update(obs, c->obj);
+     }
+}
+
+static Evas_Textblock_Obstacle *
+_obstacle_find(Evas_Textblock_Data *obj, Eo *eo_obs)
+{
+   Evas_Textblock_Obstacle *obs;
+   Eina_List *i;
+
+   EINA_LIST_FOREACH(obj->obstacles, i, obs)
+     {
+        if (eo_obs == obs->eo_obs)
+           return obs;
+     }
+   return NULL;
+}
+
+Eina_Bool
+_obstacle_del_cb(void *data, Eo *eo_obs,
+      const Eo_Event_Description *desc EINA_UNUSED,
+      void *event_info EINA_UNUSED)
+{
+   Eo *eo_obj = data;
+   Evas_Textblock_Data *obj = eo_data_scope_get(eo_obj, MY_CLASS);
+   Eina_List *i;
+   Evas_Textblock_Obstacle *obs;
+
+   EINA_LIST_FOREACH(obj->obstacles, i, obs)
+     {
+        if (eo_obs == obs->eo_obs)
+           break;
+     }
+   obj->obstacles = eina_list_remove_list(obj->obstacles, i);
+   free(obs);
+   _evas_textblock_changed(obj, data);
+   obj->obstacle_changed = EINA_TRUE;
+
+   return EINA_TRUE;
+}
+
+static void
+_obstacle_clear(Eo *eo_obj, Evas_Textblock_Obstacle *obs)
+{
+   eo_do(obs->eo_obs, eo_event_callback_del(EVAS_OBJECT_EVENT_DEL,
+            _obstacle_del_cb, eo_obj));
+}
+
+static void
+_obstacle_free(Eo *eo_obj, Evas_Textblock_Obstacle *obs)
+{
+   _obstacle_clear(eo_obj, obs);
+   free(obs);
+}
+
+static void
+_obstacles_free(Eo *eo_obj, Evas_Textblock_Data *obj)
+{
+   Evas_Textblock_Obstacle *obs;
+
+   EINA_LIST_FREE(obj->obstacles, obs)
+     {
+        _obstacle_free(eo_obj, obs);
+     }
+}
+
+EOLIAN static Eina_Bool
+_evas_textblock_obstacle_add(Eo *eo_obj,
+      Evas_Textblock_Data *obj, Eo *eo_obs)
+{
+   Evas_Textblock_Obstacle *obs;
+
+   if (!eo_isa(eo_obs, EVAS_OBJECT_CLASS))
+      return EINA_FALSE;
+   obs = _obstacle_find(obj, eo_obs);
+   if (obs) return EINA_FALSE;
+
+   obs = calloc(1, sizeof(Evas_Textblock_Obstacle));
+   if (!obs) return EINA_FALSE;
+
+   obs->eo_obs = eo_obs;
+   eo_do(eo_obs, eo_event_callback_add(EVAS_OBJECT_EVENT_DEL,_obstacle_del_cb,
+            eo_obj));
+
+   obj->obstacles = eina_list_append(obj->obstacles, obs);
+   _obstacle_update(obs, eo_obj);
+   _evas_textblock_changed(obj, eo_obj);
+   obj->obstacle_changed = EINA_TRUE;
+   return EINA_TRUE;
+}
+
+EOLIAN static Eina_Bool
+_evas_textblock_obstacle_del(Eo *eo_obj, Evas_Textblock_Data *obj,
+      Eo *eo_obs EINA_UNUSED)
+{
+   Evas_Textblock_Obstacle *obs;
+   Eina_List *i;
+
+   if (!eo_isa(eo_obs, EVAS_OBJECT_CLASS))
+      return EINA_FALSE;
+
+   EINA_LIST_FOREACH(obj->obstacles, i, obs)
+     {
+        if (eo_obs == obs->eo_obs)
+          {
+             break;
+          }
+     }
+   if (!i) return EINA_FALSE;
+   obj->obstacles = eina_list_remove_list(obj->obstacles, i);
+   _obstacle_free(eo_obj, obs);
+   _evas_textblock_changed(obj, eo_obj);
+   obj->obstacle_changed = EINA_TRUE;
+   return EINA_TRUE;
+}
+
+EOLIAN static void
+_evas_textblock_obstacles_update(Eo *eo_obj, Evas_Textblock_Data *obj)
+{
+   _evas_textblock_changed(obj, eo_obj);
+   obj->obstacle_changed = EINA_TRUE;
+}
+
+static Evas_Textblock_Obstacle *
+_layout_item_obstacle_get(Ctxt *c, Evas_Object_Textblock_Item *it)
+{
+   Evas_Textblock_Obstacle *obs, *min_obs = NULL;
+   Eina_List *i;
+
+   EINA_LIST_FOREACH(c->o->obstacles, i, obs)
+     {
+        Eina_Bool is_visible;
+        eo_do(obs->eo_obs, is_visible = efl_gfx_visible_get());
+        if (!is_visible)
+           continue;
+        if ((obs->y < c->y + it->h) &&
+            (obs->x < c->x + it->w) &&
+            (obs->x + obs->w > c->x) &&
+            (obs->y + obs->h > c->y))
+          {
+             if ((obs->x < c->w) &&
+                   (!min_obs || (obs->x < min_obs->x)))
+               {
+                  min_obs = obs;
+               }
+          }
+     }
+   return min_obs;
+}
+
 /* cursors */
 
 /**
@@ -11242,8 +11529,10 @@ evas_object_textblock_free(Evas_Object *eo_obj)
    if (o->repch) eina_stringshare_del(o->repch);
    if (o->ellip_ti) _item_free(eo_obj, NULL, _ITEM(o->ellip_ti));
   _format_command_shutdown();
-}
 
+  /* remove obstacles */
+  _obstacles_free(eo_obj, o);
+}
 
 static void
 evas_object_textblock_render(Evas_Object *eo_obj EINA_UNUSED,
@@ -11764,7 +12053,8 @@ evas_object_textblock_coords_recalc(Evas_Object *eo_obj 
EINA_UNUSED,
        // obviously if content text changed we need to reformat it
        (o->content_changed) ||
        // if format changed (eg styles) we need to re-format/match tags etc.
-       (o->format_changed)
+       (o->format_changed) ||
+       (o->obstacle_changed)
       )
      {
         LYDBG("ZZ: invalidate 2 %p ## %i != %i || %3.3f || %i && %i != %i | %i 
%i\n", eo_obj, obj->cur->geometry.w, o->last_w, o->valign, o->have_ellipsis, 
obj->cur->geometry.h, o->last_h, o->content_changed, o->format_changed);
diff --git a/src/lib/evas/canvas/evas_textblock.eo 
b/src/lib/evas/canvas/evas_textblock.eo
index 15b74c6..3832e50 100644
--- a/src/lib/evas/canvas/evas_textblock.eo
+++ b/src/lib/evas/canvas/evas_textblock.eo
@@ -297,6 +297,46 @@ class Evas.Textblock (Evas.Object)
             @in ts: Evas.Textblock.Style *; /*@ the style to set. */
          }
       }
+      obstacle_add {
+         /*@
+         Add obstacle evas object @p eo_obs to be observed during layout of 
text.
+         The textblock does the layout of the text according to the position
+         of the obstacle.
+
+         @return Returns true on success, false on failure.
+
+         @since 1.15 */
+         params {
+            @in eo_obs: Evas.Object *;
+         }
+         return: bool;
+      }
+      obstacle_del {
+         /*@
+         Removes @p eo_obs from observation during text layout
+
+         @return Returns true on success, false on failure.
+
+         @since 1.15 */
+         params {
+            @in eo_obs: Evas.Object *;
+         }
+         return: bool;
+      }
+      obstacles_update {
+         /*@
+         Triggers for relayout due to obstacles' state change. The obstacles
+         alone don't affect the layout, until this is called. Use this after
+         doing changes (moving, positioning etc.) in the obstacles that you
+         would like to be considered in the layout.
+         For example: if you have just repositioned the obstacles to differrent
+         coordinates relative to the textblock, you need to call this so
+         it will consider this new state and will relayout the text.
+
+         @return Returns no value.
+
+         @since 1.15 */
+      }
    }
    implements {
       Eo.Base.constructor;
diff --git a/src/tests/evas/evas_test_textblock.c 
b/src/tests/evas/evas_test_textblock.c
index 1c1d596..fe4dfe1 100644
--- a/src/tests/evas/evas_test_textblock.c
+++ b/src/tests/evas/evas_test_textblock.c
@@ -3304,6 +3304,93 @@ START_TEST(evas_textblock_delete)
 }
 END_TEST;
 
+/* Runs x,y in [from,to] range */
+static void
+_obstacle_run(Evas_Object *tb, Evas_Object *obj,
+      Evas_Coord from_x, Evas_Coord to_x,
+      Evas_Coord from_y, Evas_Coord to_y,
+      Evas_Coord bh)
+{
+   Evas_Coord fw, fh;
+   Evas_Coord x, y;
+   for (y = from_y; y <= to_y; y += 5)
+     {
+        for (x = from_x; x <= to_x; x += 5)
+          {
+             evas_object_move(obj, x, y);
+             evas_object_textblock_obstacles_update(tb);
+             evas_object_textblock_size_formatted_get(tb, &fw, &fh);
+             /* the obstacle size is large enough to assume that adding it
+              * will at least make the formatted height value bigger */
+             ck_assert_int_ge(fh, bh);
+          }
+     }
+}
+
+START_TEST(evas_textblock_obstacle)
+{
+   START_TB_TEST();
+   Evas_Coord fw, fh;
+   Evas_Object *rect, *rect2, *rect3;
+   const char *buf =
+      "This is an example text to demonstrate the textblock object"
+      " with obstacle objects support."
+      " Any evas object <item size=72x16></item>can register itself as an 
obstacle to the textblock"
+      " object. Upon registring, it affects the layout of the text in"
+      " certain situations. Usually, when the obstacle shows above the text"
+      " area, it will cause the layout of the text to split and move"
+      " parts of it, so that all text area is apparent.";
+
+   rect = evas_object_rectangle_add(evas);
+   rect2 = evas_object_rectangle_add(evas);
+   rect3 = evas_object_rectangle_add(evas);
+   evas_object_resize(rect, 50, 50);
+   evas_object_resize(rect2, 50, 50);
+   evas_object_resize(rect3, 50, 50);
+   evas_object_textblock_text_markup_set(tb, buf);
+   evas_textblock_cursor_format_prepend(cur, "<wrap=word>");
+   evas_object_textblock_size_formatted_get(tb, &fw, &fh);
+
+   ck_assert(!evas_object_textblock_obstacle_del(tb, rect));
+
+   ck_assert(evas_object_textblock_obstacle_add(tb, rect));
+   ck_assert(!evas_object_textblock_obstacle_add(tb, rect));
+
+   ck_assert(evas_object_textblock_obstacle_add(tb, rect2));
+   ck_assert(evas_object_textblock_obstacle_add(tb, rect3));
+
+   evas_object_show(rect);
+   evas_object_show(rect2);
+   evas_object_show(rect3);
+
+   /* Compare formatted size with and without obstacle */
+   _obstacle_run(tb, rect, 0, fw, fh / 2, fh / 2, fh);
+   /* Now, with bigger obstacles */
+   evas_object_resize(rect, 150, 150);
+   evas_object_resize(rect3, 300, 300);
+   evas_object_hide(rect);
+   evas_object_textblock_obstacles_update(tb);
+   _obstacle_run(tb, rect, 0, fw, fh / 2, fh / 2, fh);
+
+   evas_object_textblock_obstacle_del(tb, rect);
+   /* running with rect, now that it's not observed */
+   evas_textblock_cursor_format_prepend(cur, "<wrap=mixed>");
+   _obstacle_run(tb, rect, 0, fw, fh / 2, fh / 2, fh);
+
+   evas_object_del(rect2);
+   /* running with rect again, since rect2 is deleted */
+   evas_textblock_cursor_format_prepend(cur, "<wrap=char>");
+   _obstacle_run(tb, rect, 0, fw, fh / 2, fh / 2, fh);
+
+   evas_object_del(rect);
+   _obstacle_run(tb, rect3, 0, fw, 0, 0, fh);
+   END_TB_TEST();
+   /* Deleting rect3 later, so it will be first removed from observation,
+    * during freeing of the textblock */
+   evas_object_del(rect3);
+}
+END_TEST;
+
 void evas_test_textblock(TCase *tc)
 {
    tcase_add_test(tc, evas_textblock_simple);
@@ -3325,5 +3412,6 @@ void evas_test_textblock(TCase *tc)
    tcase_add_test(tc, evas_textblock_wrapping);
    tcase_add_test(tc, evas_textblock_items);
    tcase_add_test(tc, evas_textblock_delete);
+   tcase_add_test(tc, evas_textblock_obstacle);
 }
 

-- 


Reply via email to