edje_cc: introduce "Anchors" - easy way to set parts relationship
authorJeeyong Um <conr2d@gmail.com>
Tue, 25 Apr 2017 23:25:11 +0000 (16:25 -0700)
committerCedric BAIL <cedric@osg.samsung.com>
Tue, 25 Apr 2017 23:25:15 +0000 (16:25 -0700)
Test Plan: compile src/examples/edc-anchors.c and run

Reviewers: zmike, raster, cedric, jpeg

Reviewed By: raster, jpeg

Subscribers: raster, barbieri, zmike, SanghyeonLee, taxi2se, Jaehyun_Cho

Differential Revision: https://phab.enlightenment.org/D4775

Signed-off-by: Cedric BAIL <cedric@osg.samsung.com>
src/bin/edje/edje_cc.h
src/bin/edje/edje_cc_handlers.c
src/examples/edje/edje-anchors.c [new file with mode: 0644]
src/examples/edje/edje-anchors.edc [new file with mode: 0644]
src/lib/edje/edje_calc.c
src/lib/edje/edje_data.c
src/lib/edje/edje_private.h

index 9e47283..704a7fb 100644 (file)
@@ -164,6 +164,44 @@ struct _Edje_Part_Collection_Parser
    Eina_Bool inherit_only;
 };
 
+typedef enum
+{
+   EDJE_PART_ANCHOR_LINE_NONE,
+   EDJE_PART_ANCHOR_LINE_TOP,
+   EDJE_PART_ANCHOR_LINE_BOTTOM,
+   EDJE_PART_ANCHOR_LINE_LEFT,
+   EDJE_PART_ANCHOR_LINE_RIGHT,
+   EDJE_PART_ANCHOR_LINE_VERTICAL_CENTER,
+   EDJE_PART_ANCHOR_LINE_HORIZONTAL_CENTER
+} Edje_Part_Anchor_Line;
+
+typedef enum
+{
+   EDJE_PART_ANCHOR_FILL_BOTH,
+   EDJE_PART_ANCHOR_FILL_HORIZONTAL,
+   EDJE_PART_ANCHOR_FILL_VERTICAL
+} Edje_Part_Anchor_Fill;
+
+typedef struct
+{
+   union {
+      Edje_Part_Anchor_Line line;
+      Edje_Part_Anchor_Fill fill;
+   } base;
+   Eina_Bool set : 1;
+} Edje_Part_Anchor;
+
+typedef struct
+{
+   Edje_Part_Anchor top;
+   Edje_Part_Anchor bottom;
+   Edje_Part_Anchor left;
+   Edje_Part_Anchor right;
+   Edje_Part_Anchor vertical_center;
+   Edje_Part_Anchor horizontal_center;
+   Edje_Part_Anchor fill;
+} Edje_Part_Description_Anchors;
+
 /* global fn calls */
 void    data_setup(void);
 void    data_write(void);
index 8c80b19..b38ddec 100644 (file)
@@ -167,6 +167,8 @@ Eina_Bool script_override = EINA_FALSE;
 static Edje_Program *sequencing = NULL;
 static Eina_List *sequencing_lookups = NULL;
 static int *anonymous_delete = NULL;
+static Edje_Part_Description_Anchors *current_anchors = NULL;
+static Eina_Bool has_relatives = EINA_FALSE;
 
 Eina_List *po_files;
 
@@ -195,6 +197,8 @@ static void _program_target_add(char *name);
 static void _program_after(const char *name);
 static void _program_free(Edje_Program *pr);
 
+static void check_has_anchors(void);
+
 static void st_externals_external(void);
 
 static void st_images_image(void);
@@ -367,6 +371,14 @@ static void st_collections_group_parts_part_description_rel2_to_set(const char *
 static void st_collections_group_parts_part_description_rel2_to(void);
 static void st_collections_group_parts_part_description_rel2_to_x(void);
 static void st_collections_group_parts_part_description_rel2_to_y(void);
+static void st_collections_group_parts_part_description_anchors_top(void);
+static void st_collections_group_parts_part_description_anchors_bottom(void);
+static void st_collections_group_parts_part_description_anchors_left(void);
+static void st_collections_group_parts_part_description_anchors_right(void);
+static void st_collections_group_parts_part_description_anchors_vertical_center(void);
+static void st_collections_group_parts_part_description_anchors_horizontal_center(void);
+static void st_collections_group_parts_part_description_anchors_fill(void);
+static void st_collections_group_parts_part_description_anchors_margin(void);
 static void st_collections_group_parts_part_description_clip_to_id(void);
 static void st_collections_group_parts_part_description_size_class(void);
 static void st_collections_group_parts_part_description_image_normal(void);
@@ -858,6 +870,14 @@ New_Statement_Handler statement_handlers[] =
      {"collections.group.parts.part.description.rel2.to", st_collections_group_parts_part_description_rel2_to},
      {"collections.group.parts.part.description.rel2.to_x", st_collections_group_parts_part_description_rel2_to_x},
      {"collections.group.parts.part.description.rel2.to_y", st_collections_group_parts_part_description_rel2_to_y},
+     {"collections.group.parts.part.description.anchors.top", st_collections_group_parts_part_description_anchors_top},
+     {"collections.group.parts.part.description.anchors.bottom", st_collections_group_parts_part_description_anchors_bottom},
+     {"collections.group.parts.part.description.anchors.left", st_collections_group_parts_part_description_anchors_left},
+     {"collections.group.parts.part.description.anchors.right", st_collections_group_parts_part_description_anchors_right},
+     {"collections.group.parts.part.description.anchors.vertical_center", st_collections_group_parts_part_description_anchors_vertical_center},
+     {"collections.group.parts.part.description.anchors.horizontal_center", st_collections_group_parts_part_description_anchors_horizontal_center},
+     {"collections.group.parts.part.description.anchors.fill", st_collections_group_parts_part_description_anchors_fill},
+     {"collections.group.parts.part.description.anchors.margin", st_collections_group_parts_part_description_anchors_margin},
      {"collections.group.parts.part.description.clip_to", st_collections_group_parts_part_description_clip_to_id},
      {"collections.group.parts.part.description.size_class", st_collections_group_parts_part_description_size_class},
      {"collections.group.parts.part.description.image.normal", st_collections_group_parts_part_description_image_normal},
@@ -1470,6 +1490,7 @@ New_Object_Handler object_handlers[] =
      {"collections.group.parts.part.description.link", ob_collections_group_parts_part_description_link},
      {"collections.group.parts.part.description.rel1", NULL},
      {"collections.group.parts.part.description.rel2", NULL},
+     {"collections.group.parts.part.description.anchors", NULL},
      {"collections.group.parts.part.description.image", NULL}, /* dup */
      {"collections.group.parts.part.description.image.set", ob_images_set}, /* dup */
      {"collections.group.parts.part.description.image.set.image", ob_images_set_image}, /* dup */
@@ -8754,6 +8775,8 @@ st_collections_group_parts_part_description_limit(void)
 static void
 st_collections_group_parts_part_description_align(void)
 {
+   check_has_anchors();
+
    if (get_arg_count() == 2)
      {
         current_desc->align.x = FROM_DOUBLE(parse_float_range(0, 0.0, 1.0));
@@ -8784,6 +8807,7 @@ st_collections_group_parts_part_description_align(void)
 static void
 st_collections_group_parts_part_description_fixed(void)
 {
+   check_has_anchors();
    check_arg_count(2);
 
    current_desc->fixed.w = parse_float_range(0, 0, 1);
@@ -9162,6 +9186,7 @@ st_collections_group_parts_part_description_clip_to_id(void)
 static void
 st_collections_group_parts_part_description_rel1_relative(void)
 {
+   check_has_anchors();
    check_arg_count(2);
 
    current_desc->rel1.relative_x = FROM_DOUBLE(parse_float(0));
@@ -9181,6 +9206,7 @@ st_collections_group_parts_part_description_rel1_relative(void)
 static void
 st_collections_group_parts_part_description_rel1_offset(void)
 {
+   check_has_anchors();
    check_arg_count(2);
 
    current_desc->rel1.offset_x = parse_int(0);
@@ -9211,6 +9237,7 @@ st_collections_group_parts_part_description_rel1_to_set(const char *name)
 static void
 st_collections_group_parts_part_description_rel_to(void)
 {
+   check_has_anchors();
    check_arg_count(1);
 
    {
@@ -9225,6 +9252,7 @@ st_collections_group_parts_part_description_rel_to(void)
 static void
 st_collections_group_parts_part_description_rel1_to(void)
 {
+   check_has_anchors();
    check_arg_count(1);
 
    {
@@ -9252,6 +9280,7 @@ st_collections_group_parts_part_description_rel_to_x(void)
 {
    Edje_Part_Collection *pc;
 
+   check_has_anchors();
    check_arg_count(1);
 
    pc = eina_list_data_get(eina_list_last(edje_collections));
@@ -9271,6 +9300,7 @@ st_collections_group_parts_part_description_rel1_to_x(void)
 {
    Edje_Part_Collection *pc;
 
+   check_has_anchors();
    check_arg_count(1);
 
    pc = eina_list_data_get(eina_list_last(edje_collections));
@@ -9301,6 +9331,7 @@ st_collections_group_parts_part_description_rel_to_y(void)
 {
    Edje_Part_Collection *pc;
 
+   check_has_anchors();
    check_arg_count(1);
 
    pc = eina_list_data_get(eina_list_last(edje_collections));
@@ -9320,6 +9351,7 @@ st_collections_group_parts_part_description_rel1_to_y(void)
 {
    Edje_Part_Collection *pc;
 
+   check_has_anchors();
    check_arg_count(1);
 
    pc = eina_list_data_get(eina_list_last(edje_collections));
@@ -9336,6 +9368,7 @@ st_collections_group_parts_part_description_rel1_to_y(void)
 static void
 st_collections_group_parts_part_description_rel2_relative(void)
 {
+   check_has_anchors();
    check_arg_count(2);
 
    current_desc->rel2.relative_x = FROM_DOUBLE(parse_float(0));
@@ -9345,6 +9378,7 @@ st_collections_group_parts_part_description_rel2_relative(void)
 static void
 st_collections_group_parts_part_description_rel2_offset(void)
 {
+   check_has_anchors();
    check_arg_count(2);
 
    current_desc->rel2.offset_x = parse_int(0);
@@ -9363,6 +9397,7 @@ st_collections_group_parts_part_description_rel2_to_set(const char *name)
 static void
 st_collections_group_parts_part_description_rel2_to(void)
 {
+   check_has_anchors();
    check_arg_count(1);
 
    {
@@ -9378,6 +9413,7 @@ st_collections_group_parts_part_description_rel2_to_x(void)
 {
    Edje_Part_Collection *pc;
 
+   check_has_anchors();
    check_arg_count(1);
 
    pc = eina_list_data_get(eina_list_last(edje_collections));
@@ -9396,6 +9432,7 @@ st_collections_group_parts_part_description_rel2_to_y(void)
 {
    Edje_Part_Collection *pc;
 
+   check_has_anchors();
    check_arg_count(1);
 
    pc = eina_list_data_get(eina_list_last(edje_collections));
@@ -9409,6 +9446,568 @@ st_collections_group_parts_part_description_rel2_to_y(void)
    }
 }
 
+/** @edcsubsection{collections_group_parts_description_anchors,
+ *                 Group.Parts.Part.Description.Anchors} */
+
+/**
+    @page edcref
+    @block
+        anchors
+    @context
+        // This part will be expanded from the top-left corner of edje group
+        part { name : "part1";
+            description { state: "default" 0.0;
+                anchors {
+                    top: GROUP TOP;
+                    left: GROUP; // This means 'left: GROUP LEFT;'
+                }
+                min: 50 50;
+            }
+        }
+        // This part will be expanded from the bottom-right corner of "part1"
+        // to the bottom-right
+        part { name: "part2";
+            description { state: "default" 0.0;
+                anchors {
+                    top: "part1" BOTTOM;
+                    left: "part1"; // This means 'left: "part1" RIGHT;'
+                }
+                min: 50 50;
+            }
+        }
+        // This part will be expanded from the right edje of "part2" to the right
+        part { name: "part3";
+            description { state: "default" 0.0;
+                anchors {
+                    left: "part2";
+                    fill: "part2" VERTICAL;
+                }
+                min: 100 0; // The height will be determined by the height of "part2"
+            }
+        }
+        // This part will be expanded from the center of right edge of "part3"
+        // to the bottom-right corner of edje group
+        part { name: "part4";
+            description { state: "default" 0.0;
+                anchors {
+                    top: "part3" VERTICAL_CENTER;
+                    left: "part3";
+                    right: GROUP;
+                    bottom: GROUP;
+                }
+            }
+        }
+    @description
+        The anchors blocks are used to define the position of each edge of
+        the part's container. Anchors will change relative, align and fixed
+        attributes internally, so setting both of them is not allowed.
+        When the second parameter of position enumeration is omitted, anchoring
+        a part to the other part will put the part adjacent to the given part.
+        However, if the part is anchored to edje group, the part will be contained
+        inside the group.
+    @endblock
+
+    @property
+        anchors
+    @parameters
+        [partname] [the edge of other part]
+    @effect
+        Moves an edge of the part to the position of the edge of given part or
+        whole edje group. (GROUP means edje group that the part belong to)
+    @endproperty
+*/
+
+static void
+check_has_anchors(void)
+{
+   if (current_anchors)
+     {
+        ERR("parse error %s:%i. Anchors and Relatives(rel/align/fixed) cannot be used at the same time.",
+            file_in, line - 1);
+        exit(-1);
+     }
+
+   has_relatives = EINA_TRUE;
+}
+
+static void
+check_has_relatives(void)
+{
+   if (has_relatives)
+     {
+        ERR("parse error %s:%i. Anchors and Relatives(rel/align/fixed) cannot be used at the same time.",
+            file_in, line - 1);
+        exit(-1);
+     }
+
+   current_desc->offset_is_scaled = EINA_TRUE;
+}
+
+static void
+parse_anchor_line(Edje_Part_Anchor *anchor, Edje_Part_Anchor_Line undefined)
+{
+   int nargs;
+   char *name;
+
+   nargs = get_arg_count();
+   if (!nargs || (nargs > 2))
+     {
+        ERR("parse error %s:%i. Anchors should have a name of part and base line.",
+            file_in, line - 1);
+        exit(-1);
+     }
+
+   name = parse_str(0);
+   anchor->set = EINA_TRUE;
+
+   if (nargs == 2)
+     anchor->base.line = parse_enum(1,
+                                    "TOP", EDJE_PART_ANCHOR_LINE_TOP,
+                                    "BOTTOM", EDJE_PART_ANCHOR_LINE_BOTTOM,
+                                    "LEFT", EDJE_PART_ANCHOR_LINE_LEFT,
+                                    "RIGHT", EDJE_PART_ANCHOR_LINE_RIGHT,
+                                    "VERTICAL_CENTER", EDJE_PART_ANCHOR_LINE_VERTICAL_CENTER,
+                                    "HORIZONTAL_CENTER", EDJE_PART_ANCHOR_LINE_HORIZONTAL_CENTER,
+                                    NULL);
+   else if (strcmp(name, "GROUP"))
+     anchor->base.line = undefined;
+}
+
+static void
+parse_anchor_fill(Edje_Part_Anchor *anchor)
+{
+   int nargs;
+
+   nargs = get_arg_count();
+   if (!nargs || (nargs > 2))
+     {
+        ERR("parse error %s:%i. Anchors should have a name of part and base line.",
+            file_in, line - 1);
+        exit(-1);
+     }
+
+   anchor->set = EINA_TRUE;
+
+   if (nargs == 2)
+     anchor->base.fill = parse_enum(1,
+                                    "BOTH", EDJE_PART_ANCHOR_FILL_BOTH,
+                                    "HORIZONTAL", EDJE_PART_ANCHOR_FILL_HORIZONTAL,
+                                    "VERTICAL", EDJE_PART_ANCHOR_FILL_VERTICAL,
+                                    NULL);
+   else
+     anchor->base.fill = EDJE_PART_ANCHOR_FILL_BOTH;
+}
+
+static void
+anchor_queue_part_lookup(int *part, int *counterpart, Eina_Bool counterpart_is_set)
+{
+   Edje_Part_Collection *pc;
+   char *name;
+
+   pc = eina_list_data_get(eina_list_last(edje_collections));
+
+   name = parse_str(0);
+   if (!strcmp(name, "GROUP")) return;
+
+   data_queue_part_lookup(pc, name, part);
+
+   if (!counterpart_is_set)
+     data_queue_part_lookup(pc, name, counterpart);
+
+   free(name);
+}
+
+static void
+anchor_dequeue_part_lookup(int *part, Eina_Bool counterpart_is_set)
+{
+   Edje_Part_Collection *pc;
+
+   pc = eina_list_data_get(eina_list_last(edje_collections));
+
+   if (counterpart_is_set && part)
+     part_lookup_del(pc, part);
+}
+
+static void
+anchor_adjust_align(FLOAT_T *align, FLOAT_T val, unsigned char *fixed, Eina_Bool counterpart_is_set)
+{
+   if (counterpart_is_set)
+     {
+        *align = 0.5;
+        *fixed = 0;
+     }
+   else
+     {
+        *align = val;
+        *fixed = 1;
+     }
+}
+
+static void
+anchor_adjust_relative(Edje_Part_Anchor_Line *lines, FLOAT_T *rel, FLOAT_T *relc, Edje_Part_Anchor_Line line, Edje_Part_Anchor_Line base, Eina_Bool counterpart_is_set)
+{
+   if (line == EDJE_PART_ANCHOR_LINE_NONE)
+     line = base;
+
+   if (line == lines[0])
+     {
+        *rel = FROM_DOUBLE(0.0);
+        if (!counterpart_is_set)
+          *relc = FROM_DOUBLE(0.0);
+     }
+   else if (line == lines[1])
+     {
+        *rel = FROM_DOUBLE(1.0);
+        if (!counterpart_is_set)
+          *relc = FROM_DOUBLE(1.0);
+     }
+   else if (line == lines[2])
+     {
+        *rel = FROM_DOUBLE(0.5);
+        if (!counterpart_is_set)
+          *relc = FROM_DOUBLE(0.5);
+     }
+   else
+     {
+        ERR("parse error %s:%i. Edje part is anchored to wrong position.",
+            file_in, line - 1);
+        exit(-1);
+     }
+}
+
+static void
+anchor_adjust_relative_vertical(FLOAT_T *rel, FLOAT_T *relc, Edje_Part_Anchor_Line line, Edje_Part_Anchor_Line base, Eina_Bool counterpart_is_set)
+{
+   static const Edje_Part_Anchor_Line lines[] = {
+      EDJE_PART_ANCHOR_LINE_TOP,
+      EDJE_PART_ANCHOR_LINE_BOTTOM,
+      EDJE_PART_ANCHOR_LINE_VERTICAL_CENTER
+   };
+
+   anchor_adjust_relative(lines, rel, relc, line, base, counterpart_is_set);
+}
+
+static void
+anchor_adjust_relative_horizontal(FLOAT_T *rel, FLOAT_T *relc, Edje_Part_Anchor_Line line, Edje_Part_Anchor_Line base, Eina_Bool counterpart_is_set)
+{
+   static const Edje_Part_Anchor_Line lines[] = {
+      EDJE_PART_ANCHOR_LINE_LEFT,
+      EDJE_PART_ANCHOR_LINE_RIGHT,
+      EDJE_PART_ANCHOR_LINE_HORIZONTAL_CENTER
+   };
+
+   anchor_adjust_relative(lines, rel, relc, line, base, counterpart_is_set);
+}
+
+/**
+    @page edcref
+    @property
+        top
+    @parameters
+        [partname] [TOP/BOTTOM/VERTICAL_CENTER]
+    @effect
+        Causes top edge to be positioned to the edge of another part's container.
+        Setting to GROUP will indicate edje group instead of another part.
+        If bottom anchor is not set, edje part will be expanded to the bottom.
+        The second parameter of position enumeration can be omitted. (Default
+        value is BOTTOM, but TOP when the part is anchored to edje group)
+    @endproperty
+*/
+static void
+st_collections_group_parts_part_description_anchors_top(void)
+{
+   Eina_Bool counterpart_is_set;
+
+   check_has_relatives();
+
+   if (!current_anchors)
+     current_anchors = mem_alloc(SZ(Edje_Part_Description_Anchors));
+
+   counterpart_is_set = current_anchors->bottom.set;
+
+   parse_anchor_line(&(current_anchors->top), EDJE_PART_ANCHOR_LINE_BOTTOM);
+
+   anchor_dequeue_part_lookup(&(current_desc->rel1.id_y), counterpart_is_set);
+   anchor_queue_part_lookup(&(current_desc->rel1.id_y), &(current_desc->rel2.id_y), counterpart_is_set);
+
+   anchor_adjust_align(&(current_desc->align.y), 0.0, &(current_desc->fixed.h), counterpart_is_set);
+   anchor_adjust_relative_vertical(&(current_desc->rel1.relative_y), &(current_desc->rel2.relative_y), current_anchors->top.base.line, EDJE_PART_ANCHOR_LINE_TOP, counterpart_is_set);
+}
+
+/**
+    @page edcref
+    @property
+        bottom
+    @parameters
+        [partname] [TOP/BOTTOM/VERTICAL_CENTER]
+    @effect
+        Causes bottom edge to be positioned to the edge of another part's container.
+        Setting to GROUP will indicate edje group instead of another part.
+        If top anchor is not set, edje part will be expanded to the top.
+        The second parameter of position enumeration can be omitted. (Default
+        value is TOP, but BOTTOM when the part is anchored to edje group)
+    @endproperty
+*/
+static void
+st_collections_group_parts_part_description_anchors_bottom(void)
+{
+   Eina_Bool counterpart_is_set;
+
+   check_has_relatives();
+
+   if (!current_anchors)
+     current_anchors = mem_alloc(SZ(Edje_Part_Description_Anchors));
+
+   counterpart_is_set = current_anchors->top.set;
+
+   parse_anchor_line(&(current_anchors->bottom), EDJE_PART_ANCHOR_LINE_TOP);
+
+   anchor_dequeue_part_lookup(&(current_desc->rel2.id_y), counterpart_is_set);
+   anchor_queue_part_lookup(&(current_desc->rel2.id_y), &(current_desc->rel1.id_y), counterpart_is_set);
+
+   anchor_adjust_align(&(current_desc->align.y), 1.0, &(current_desc->fixed.h), counterpart_is_set);
+   anchor_adjust_relative_vertical(&(current_desc->rel2.relative_y), &(current_desc->rel1.relative_y), current_anchors->bottom.base.line, EDJE_PART_ANCHOR_LINE_BOTTOM, counterpart_is_set);
+}
+
+/**
+    @page edcref
+    @property
+        left
+    @parameters
+        [partname] [LEFT/RIGHT/HORIZONTAL_CENTER]
+    @effect
+        Causes left edge to be positioned to the edge of another part's container.
+        Setting to GROUP will indicate edje group instead of another part.
+        If right anchor is not set, edje part will be expanded to the right.
+        The second parameter of position enumeration can be omitted. (Default
+        value is RIGHT, but LEFT when the part is anchored to edje group)
+    @endproperty
+*/
+static void
+st_collections_group_parts_part_description_anchors_left(void)
+{
+   Eina_Bool counterpart_is_set;
+
+   check_has_relatives();
+
+   if (!current_anchors)
+     current_anchors = mem_alloc(SZ(Edje_Part_Description_Anchors));
+
+   counterpart_is_set = current_anchors->right.set;
+
+   parse_anchor_line(&(current_anchors->left), EDJE_PART_ANCHOR_LINE_RIGHT);
+
+   anchor_dequeue_part_lookup(&(current_desc->rel1.id_x), counterpart_is_set);
+   anchor_queue_part_lookup(&(current_desc->rel1.id_x), &(current_desc->rel2.id_x), counterpart_is_set);
+
+   anchor_adjust_align(&(current_desc->align.x), 0.0, &(current_desc->fixed.w), counterpart_is_set);
+   anchor_adjust_relative_horizontal(&(current_desc->rel1.relative_x), &(current_desc->rel2.relative_x), current_anchors->left.base.line, EDJE_PART_ANCHOR_LINE_LEFT, counterpart_is_set);
+}
+
+/**
+    @page edcref
+    @property
+        right
+    @parameters
+        [partname] [LEFT/RIGHT/HORIZONTAL_CENTER]
+    @effect
+        Causes right edge to be positioned to the edge of another part's container.
+        Setting to GROUP will indicate edje group instead of another part.
+        If left anchor is not set, edje part will be expanded to the left.
+        The second parameter of position enumeration can be omitted. (Default
+        value is LEFT, but RIGHT when the part is anchored to edje group)
+    @endproperty
+*/
+static void
+st_collections_group_parts_part_description_anchors_right(void)
+{
+   Eina_Bool counterpart_is_set;
+
+   check_has_relatives();
+
+   if (!current_anchors)
+     current_anchors = mem_alloc(SZ(Edje_Part_Description_Anchors));
+
+   counterpart_is_set = current_anchors->left.set;
+
+   parse_anchor_line(&(current_anchors->right), EDJE_PART_ANCHOR_LINE_LEFT);
+
+   anchor_dequeue_part_lookup(&(current_desc->rel2.id_x), counterpart_is_set);
+   anchor_queue_part_lookup(&(current_desc->rel2.id_x), &(current_desc->rel1.id_x), counterpart_is_set);
+
+   anchor_adjust_align(&(current_desc->align.x), 1.0, &(current_desc->fixed.w), counterpart_is_set);
+   anchor_adjust_relative_horizontal(&(current_desc->rel2.relative_x), &(current_desc->rel1.relative_x), current_anchors->right.base.line, EDJE_PART_ANCHOR_LINE_RIGHT, counterpart_is_set);
+}
+
+/**
+    @page edcref
+    @property
+        vertical_center
+    @parameters
+        [partname] [TOP/BOTTOM/VERTICAL_CENTER]
+    @effect
+        Causes (virtual) vertical center line to be positioned to the edge of
+        another part's container. Setting to GROUP will indicate edje group instead
+        of another part.
+        This part will be expanded vertically in both directions, so do not
+        set top or bottom anchor with vertical_center anchor.
+        The second parameter of position enumeration can be omitted. (Default
+        value is VERTICAL_CENTER)
+    @endproperty
+*/
+static void
+st_collections_group_parts_part_description_anchors_vertical_center(void)
+{
+   check_has_relatives();
+
+   if (!current_anchors)
+     current_anchors = mem_alloc(SZ(Edje_Part_Description_Anchors));
+
+   parse_anchor_line(&(current_anchors->vertical_center), EDJE_PART_ANCHOR_LINE_VERTICAL_CENTER);
+
+   anchor_queue_part_lookup(&(current_desc->rel1.id_y), &(current_desc->rel2.id_y), EINA_FALSE);
+
+   anchor_adjust_align(&(current_desc->align.y), 0.5, &(current_desc->fixed.h), EINA_FALSE);
+   anchor_adjust_relative_vertical(&(current_desc->rel1.relative_y), &(current_desc->rel2.relative_y), current_anchors->vertical_center.base.line, EDJE_PART_ANCHOR_LINE_VERTICAL_CENTER, EINA_FALSE);
+}
+
+/**
+    @page edcref
+    @property
+        horizontal_center
+    @parameters
+        [partname] [LEFT/RIGHT/HORIZONTAL_CENTER]
+    @effect
+        Causes (virtual) horizontal center line to be positioned to the edge of
+        another part's container. Setting to GROUP will indicate edje group instead
+        of another part.
+        This part will be expanded horizontally in both directions, so do not
+        set left or right anchor with vertical_center anchor.
+        The second parameter of position enumeration can be omitted. (Default
+        value is HORIZONTAL_CENTER)
+    @endproperty
+*/
+static void
+st_collections_group_parts_part_description_anchors_horizontal_center(void)
+{
+   check_has_relatives();
+
+   if (!current_anchors)
+     current_anchors = mem_alloc(SZ(Edje_Part_Description_Anchors));
+
+   parse_anchor_line(&(current_anchors->horizontal_center), EDJE_PART_ANCHOR_LINE_HORIZONTAL_CENTER);
+
+   anchor_queue_part_lookup(&(current_desc->rel1.id_x), &(current_desc->rel2.id_x), EINA_FALSE);
+
+   anchor_adjust_align(&(current_desc->align.x), 0.5, &(current_desc->fixed.w), EINA_FALSE);
+   anchor_adjust_relative_horizontal(&(current_desc->rel1.relative_x), &(current_desc->rel2.relative_x), current_anchors->horizontal_center.base.line, EDJE_PART_ANCHOR_LINE_HORIZONTAL_CENTER, EINA_FALSE);
+}
+
+/**
+    @page edcref
+    @property
+        fill
+    @parameters
+        [partname] [BOTH/HORIZONTAL/VERTICAL]
+    @effect
+        Causes the part's container to expand to the width or height of another
+        part's container. Setting to GROUP will indicate edje group instead of another part.
+        Setting horizontal fill has same effect to setting top and bottom anchors
+        to the same part.
+        (setting vertical fill means left and right anchors to the same part)
+        The second parameter of direction enumeration can be omitted. (Default
+        value is BOTH)
+    @endproperty
+*/
+static void
+st_collections_group_parts_part_description_anchors_fill(void)
+{
+   Edje_Part_Collection *pc;
+   char *name;
+
+   pc = eina_list_last_data_get(edje_collections);
+
+   check_has_relatives();
+
+   if (!current_anchors)
+     current_anchors = mem_alloc(SZ(Edje_Part_Description_Anchors));
+
+   parse_anchor_fill(&(current_anchors->fill));
+
+   name = parse_str(0);
+
+   switch (current_anchors->fill.base.fill)
+     {
+      case EDJE_PART_ANCHOR_FILL_BOTH:
+         if (strcmp("GROUP", name))
+           {
+              data_queue_part_lookup(pc, name, &(current_desc->rel1.id_x));
+              data_queue_part_lookup(pc, name, &(current_desc->rel2.id_x));
+              data_queue_part_lookup(pc, name, &(current_desc->rel1.id_y));
+              data_queue_part_lookup(pc, name, &(current_desc->rel2.id_y));
+           }
+         current_desc->align.x = 0.5;
+         current_desc->align.y = 0.5;
+         current_desc->fixed.w = 0;
+         current_desc->fixed.h = 0;
+         break;
+      case EDJE_PART_ANCHOR_FILL_HORIZONTAL:
+         if (strcmp("GROUP", name))
+           {
+              data_queue_part_lookup(pc, name, &(current_desc->rel1.id_x));
+              data_queue_part_lookup(pc, name, &(current_desc->rel2.id_x));
+           }
+         current_desc->align.x = 0.5;
+         current_desc->fixed.w = 0;
+         break;
+      case EDJE_PART_ANCHOR_FILL_VERTICAL:
+         if (strcmp("GROUP", name))
+           {
+              data_queue_part_lookup(pc, name, &(current_desc->rel1.id_y));
+              data_queue_part_lookup(pc, name, &(current_desc->rel2.id_y));
+           }
+         current_desc->align.y = 0.5;
+         current_desc->fixed.h = 0;
+         break;
+     }
+
+   free(name);
+}
+
+/**
+    @page edcref
+    @property
+        margin
+    @parameters
+        [left] [right] [top] [bottom]
+    @effect
+        Affects the edge position a fixed number of pixels along each direction.
+        Margins will scale its size with an edje scaling factor.
+    @endproperty
+*/
+static void
+st_collections_group_parts_part_description_anchors_margin(void)
+{
+   check_has_relatives();
+   check_arg_count(4);
+
+   current_desc->rel1.offset_x = parse_int(0);
+   current_desc->rel2.offset_x = -parse_int(1) - 1;
+   current_desc->rel1.offset_y = parse_int(2);
+   current_desc->rel2.offset_y = -parse_int(3) - 1;
+}
+
+static void
+free_anchors(void)
+{
+   has_relatives = EINA_FALSE;
+
+   if (!current_anchors) return;
+
+   free(current_anchors);
+   current_anchors = NULL;
+}
+
 /** @edcsubsection{collections_group_parts_description_image,
  *                 Group.Parts.Part.Description.Image} */
 
@@ -15154,6 +15753,8 @@ edje_cc_handlers_pop_notify(const char *token)
      current_program = NULL;
    else if (current_de && (!strcmp(token, "group")))
      _link_combine();
+   else if (current_desc && (!strcmp(token, "description")))
+     free_anchors();
 }
 
 static void
diff --git a/src/examples/edje/edje-anchors.c b/src/examples/edje/edje-anchors.c
new file mode 100644 (file)
index 0000000..135f0ab
--- /dev/null
@@ -0,0 +1,58 @@
+/**
+ * Simple Edje example for layouting parts with anchors.
+ *
+ * You'll need at least one Evas engine built for it (excluding the
+ * buffer one). See stdout/stderr for output.
+ *
+ * @verbatim
+ * edje_cc edje-anchors.edc && gcc -o edje-anchors edje-anchors.c `pkg-config --libs --cflags evas ecore ecore-evas edje`
+ * @endverbatim
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#else
+# define EINA_UNUSED
+#endif
+
+#include <Ecore.h>
+#include <Ecore_Evas.h>
+#include <Edje.h>
+#include <libgen.h>
+
+#define WIDTH 400
+#define HEIGHT 400
+
+int
+main(int argc, char **argv)
+{
+   char path[PATH_MAX] = { 0, };
+
+   ecore_init();
+   ecore_evas_init();
+   edje_init();
+
+   Ecore_Evas *ee = ecore_evas_new(NULL, 0, 0, WIDTH, HEIGHT, NULL);
+   Evas *e = ecore_evas_get(ee);
+   ecore_evas_show(ee);
+
+   Evas_Object *bg = evas_object_rectangle_add(e);
+   evas_object_color_set(bg, 64, 64, 64, 255);
+   evas_object_resize(bg, WIDTH, HEIGHT);
+   evas_object_show(bg);
+
+   snprintf(path, sizeof(path), "%s/edje-anchors.edj", dirname(*argv));
+
+   Evas_Object *edje = edje_object_add(e);
+   edje_object_file_set(edje, path, "main");
+   evas_object_resize(edje, WIDTH, HEIGHT);
+   evas_object_show(edje);
+
+   ecore_main_loop_begin();
+
+   edje_shutdown();
+   ecore_evas_shutdown();
+   ecore_shutdown();
+
+   return 0;
+}
diff --git a/src/examples/edje/edje-anchors.edc b/src/examples/edje/edje-anchors.edc
new file mode 100644 (file)
index 0000000..2d3d485
--- /dev/null
@@ -0,0 +1,58 @@
+collections {
+   group { "main";
+      parts {
+         rect { "rect1";
+            desc {
+               anchors.top: GROUP TOP;
+               anchors.left: GROUP; // anchors.left: GROUP LEFT;
+               color: "#f00";
+               min: 50 50;
+            }
+         }
+         rect { "rect2";
+            desc {
+               anchors.top: "rect1" BOTTOM;
+               anchors.left: "rect1" RIGHT;
+               color: "#00f";
+               min: 50 50;
+            }
+         }
+         rect { "rect3";
+            desc {
+               anchors.left: "rect2"; // anchors.left: "rect1" RIGHT;
+               anchors.fill: "rect2" VERTICAL;
+               anchors.margin: 20 0 0 0;
+               color: "#0f0";
+               min: 100 0;
+            }
+         }
+         rect { "rect4";
+            desc {
+               anchors {
+                  top: "rect3";
+                  left: "rect3";
+                  right: GROUP;
+                  bottom: GROUP;
+                  margin: 0 10 0 50;
+               }
+            }
+         }
+         rect { "rect5";
+            desc {
+               anchors.vertical_center: "rect4";
+               anchors.fill: "rect4" HORIZONTAL;
+               min: 0 50;
+               color: "#000";
+            }
+         }
+         rect { "rect6";
+            desc {
+               anchors.right: "rect5" HORIZONTAL_CENTER;
+               anchors.fill: "rect5" VERTICAL;
+               min: 30 0;
+               color: "#ff0";
+            }
+         }
+      }
+   }
+}
index bf0d809..37163c3 100644 (file)
@@ -1114,42 +1114,71 @@ _edje_part_recalc_single_rel(Edje *ed,
 {
    FLOAT_T x, w;
    FLOAT_T y, h;
+   FLOAT_T offset, sc;
+   Eina_Bool offset_is_scaled = (desc->offset_is_scaled) && (ep->part->scale);
+
+   if (offset_is_scaled)
+     {
+        sc = DIV(ed->scale, ed->file->base_scale);
+        if (EQ(sc, ZERO)) sc = DIV(_edje_scale, ed->file->base_scale);
+     }
+
+   if (offset_is_scaled)
+     offset = SCALE(sc, desc->rel1.offset_x);
+   else
+     offset = FROM_INT(desc->rel1.offset_x);
 
    if (rel1_to_x)
-     x = ADD(FROM_INT(desc->rel1.offset_x + rel1_to_x->x),
+     x = ADD(ADD(offset, FROM_INT(rel1_to_x->x)),
              SCALE(desc->rel1.relative_x, rel1_to_x->w));
    else
-     x = ADD(FROM_INT(desc->rel1.offset_x),
+     x = ADD(offset,
              SCALE(desc->rel1.relative_x, ed->w));
    params->eval.x = x;
 
+   if (offset_is_scaled)
+     offset = SUB(SCALE(sc, desc->rel2.offset_x + 1), FROM_INT(1));
+   else
+     offset = FROM_INT(desc->rel2.offset_x);
+
    if (rel2_to_x)
-     w = ADD(SUB(ADD(FROM_INT(desc->rel2.offset_x + rel2_to_x->x),
+     w = ADD(SUB(ADD(ADD(offset, FROM_INT(rel2_to_x->x)),
                      SCALE(desc->rel2.relative_x, rel2_to_x->w)),
                  x),
              FROM_INT(1));
    else
-     w = ADD(SUB(ADD(FROM_INT(desc->rel2.offset_x),
+     w = ADD(SUB(ADD(offset,
                      SCALE(desc->rel2.relative_x, ed->w)),
                  x),
              FROM_INT(1));
    params->eval.w = w;
 
+
+   if (offset_is_scaled)
+     offset = SCALE(sc, desc->rel1.offset_y);
+   else
+     offset = FROM_INT(desc->rel1.offset_y);
+
    if (rel1_to_y)
-     y = ADD(FROM_INT(desc->rel1.offset_y + rel1_to_y->y),
+     y = ADD(ADD(offset, FROM_INT(rel1_to_y->y)),
              SCALE(desc->rel1.relative_y, rel1_to_y->h));
    else
-     y = ADD(FROM_INT(desc->rel1.offset_y),
+     y = ADD(offset,
              SCALE(desc->rel1.relative_y, ed->h));
    params->eval.y = y;
 
+   if (offset_is_scaled)
+     offset = SUB(SCALE(sc, desc->rel2.offset_y + 1), FROM_INT(1));
+   else
+     offset = FROM_INT(desc->rel2.offset_y);
+
    if (rel2_to_y)
-     h = ADD(SUB(ADD(FROM_INT(desc->rel2.offset_y + rel2_to_y->y),
+     h = ADD(SUB(ADD(ADD(offset, FROM_INT(rel2_to_y->y)),
                      SCALE(desc->rel2.relative_y, rel2_to_y->h)),
                  y),
              FROM_INT(1));
    else
-     h = ADD(SUB(ADD(FROM_INT(desc->rel2.offset_y),
+     h = ADD(SUB(ADD(offset,
                      SCALE(desc->rel2.relative_y, ed->h)),
                  y),
              FROM_INT(1));
@@ -4577,6 +4606,7 @@ _edje_part_recalc(Edje *ed, Edje_Real_Part *ep, int flags, Edje_Calc_Params *sta
                                     rp1[Rel2X], rp1[Rel2Y], clip1, confine_to,
                                     threshold, p1, mmw, mmh,
                                     pos);
+
 #ifdef EDJE_CALC_CACHE
            if (flags == FLAG_XY) ep->param1.state = ed->state;
 #endif
@@ -4622,6 +4652,7 @@ _edje_part_recalc(Edje *ed, Edje_Real_Part *ep, int flags, Edje_Calc_Params *sta
                                     rp2[Rel2X], rp2[Rel2Y], clip2, confine_to,
                                     threshold, p2, mmw, mmh,
                                     pos);
+
 #ifdef EDJE_CALC_CACHE
            if (flags == FLAG_XY) ep->param2->state = ed->state;
 #endif
@@ -4705,7 +4736,6 @@ _edje_part_recalc(Edje *ed, Edje_Real_Part *ep, int flags, Edje_Calc_Params *sta
         p3->smooth = (beginning_pos) ? p1->smooth : p2->smooth;
 
         /* FIXME: do x and y separately base on flag */
-
         p3->final.x = INTP(p1->final.x, p2->final.x, pos);
         p3->final.y = INTP(p1->final.y, p2->final.y, pos);
         p3->final.w = INTP(p1->final.w, p2->final.w, pos);
index e6d2fbb..5e3ffe5 100644 (file)
@@ -1217,7 +1217,8 @@ _edje_edd_init(void)
   EET_DATA_DESCRIPTOR_ADD_BASIC(Edd, Type, "map.zoom.x", map.zoom.x, EDJE_T_FLOAT);                            \
   EET_DATA_DESCRIPTOR_ADD_BASIC(Edd, Type, "map.zoom.y", map.zoom.y, EDJE_T_FLOAT);                            \
   EET_DATA_DESCRIPTOR_ADD_BASIC(Edd, Type, "persp.zplane", persp.zplane, EET_T_INT);                           \
-  EET_DATA_DESCRIPTOR_ADD_BASIC(Edd, Type, "persp.focal", persp.focal, EET_T_INT);
+  EET_DATA_DESCRIPTOR_ADD_BASIC(Edd, Type, "persp.focal", persp.focal, EET_T_INT);                             \
+  EET_DATA_DESCRIPTOR_ADD_BASIC(Edd, Type, "offset_is_scaled", offset_is_scaled, EET_T_UCHAR);
 
 #ifdef HAVE_EPHYSICS
 #define EDJE_DATA_DESCRIPTOR_DESCRIPTION_COMMON(Edd, Type)                                                          \
@@ -1311,6 +1312,7 @@ _edje_edd_init(void)
   EET_DATA_DESCRIPTOR_ADD_BASIC(Edd, Type, "map.zoom.y", Dec.map.zoom.y, EDJE_T_FLOAT);                            \
   EET_DATA_DESCRIPTOR_ADD_BASIC(Edd, Type, "persp.zplane", Dec.persp.zplane, EET_T_INT);                           \
   EET_DATA_DESCRIPTOR_ADD_BASIC(Edd, Type, "persp.focal", Dec.persp.focal, EET_T_INT);                             \
+  EET_DATA_DESCRIPTOR_ADD_BASIC(Edd, Type, "offset_is_scaled", Dec.offset_is_scaled, EET_T_UCHAR);
 
 #ifdef HAVE_EPHYSICS
 #define EDJE_DATA_DESCRIPTOR_DESCRIPTION_COMMON_SUB(Edd, Type, Dec)                                                     \
index b5cd554..94c994a 100644 (file)
@@ -1330,9 +1330,11 @@ struct _Edje_Part_Description_Common
 #endif
 
    Edje_3D_Vec       align_3d;
+
    unsigned char     visible; /* is it shown */
    unsigned char     limit; /* 0 == no, 1 = width, 2 = height, 3 = both */
    unsigned char     no_render; /* no_render override @since 1.19 */
+   unsigned char     offset_is_scaled;
 };
 
 struct _Edje_Part_Description_Spec_Fill