This is an automated email from the git hooks/post-receive script.

git pushed a commit to branch edje-vector-intergration
in repository efl.

View the commit online.

commit fac2d00ca6a633598ab63c1bfcf66da30924b5b1
Author: [email protected] <[email protected]>
AuthorDate: Wed Apr 29 09:27:45 2026 -0600

    edje: implement override-list inheritance with merge (Task 4.4)
    
    This patch completes override inheritance semantics (spec §4.4-4.6). When
    a child description inherits from a parent VECTOR description, the parent's
    override list is deep-copied and the child's own override blocks merge on
    a per-target basis.
    
    Key changes:
    
    - _edje_vg_override_dup / _edje_vg_override_free added to edje_vg_tree.{c,h}:
      field-by-field deep clone keyed by field_mask, with stringshare ref
      accounting and malloc+memcpy for arrays.
    
    - _merge_override_fields static helper mirrors dup logic but merges into
      an existing override, using eina_stringshare_replace to handle ref-count
      math correctly across old-release and new-ref.
    
    - _vg_open_override no longer appends the placeholder. Append (or merge)
      is deferred to st_vg_override_target so merge decision is informed by
      the resolved target name.
    
    - st_vg_override_target does merge-or-append: walks ed->vg.overrides for
      (target_name, expected_type) match; on match, applies pre-target fields
      via _merge_override_fields and re-routes current_vg_override; on miss,
      replaces placeholder's target and appends.
    
    - Description-inherit handler resets ied->vg.overrides to NULL after
      shallow copy, then deep-copies each parent entry. Prevents corruption.
    
    - Orphan-placeholder check in _vg_close_block emits error if an override
      block closes without target: statement. Restores diagnostic lost when
      deferred-append moved the placeholder.
    
    Test fixture: ui/box_inherit_merge exercises inherit + merge on same target.
    52/52 tests pass.
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
---
 src/bin/edje/edje_cc_handlers.c            | 253 +++++++++++++++++++++++++++--
 src/lib/edje/edje_vg_tree.c                | 229 ++++++++++++++++++++++++++
 src/lib/edje/edje_vg_tree.h                |  13 ++
 src/tests/edje/data/test_vector_states.edc |  24 +++
 4 files changed, 507 insertions(+), 12 deletions(-)

diff --git a/src/bin/edje/edje_cc_handlers.c b/src/bin/edje/edje_cc_handlers.c
index cf36c20b05..6637e636b3 100644
--- a/src/bin/edje/edje_cc_handlers.c
+++ b/src/bin/edje/edje_cc_handlers.c
@@ -1854,29 +1854,24 @@ _vg_close_node(void)
      current_vg_node = current_vg_tree ? current_vg_tree->root : NULL;
 }
 
-/* Task 4.3: Open an override block at description scope.
- * Allocates an Edje_Vg_Override, appends it to the description's list,
- * and routes subsequent property statements into its payload.
- * target: must be set by st_vg_override_target before description close.
- * Note: Task 4.4 will replace the append with a merge-or-append path. */
+/* Task 4.4: Open an override block at description scope.
+ * Allocates a placeholder Edje_Vg_Override but does NOT append it yet.
+ * The append (or merge into an existing inherited entry) is deferred to
+ * st_vg_override_target, which fires when the target: statement is parsed. */
 static void
 _vg_open_override(Edje_Vg_Node_Type type)
 {
-   Edje_Part_Description_Vector *ed;
    Edje_Vg_Override *ovr;
 
    if (!current_part || current_part->type != EDJE_PART_TYPE_VECTOR)
      error_and_abort(NULL, "vector override outside VECTOR description");
 
-   ed = (Edje_Part_Description_Vector *)current_desc;
-
    ovr = calloc(1, sizeof(Edje_Vg_Override));
    if (!ovr) error_and_abort(NULL, "OOM allocating override");
    ovr->expected_type = type;
    /* payload zeroed; target_name still NULL.
-    * Append happens immediately here; Task 4.4 will swap this for the
-    * merge-aware path once target: fires. */
-   ed->vg.overrides = eina_list_append(ed->vg.overrides, ovr);
+    * Do NOT append to ed->vg.overrides yet — st_vg_override_target will
+    * decide whether to merge with an existing inherited entry or append. */
 
    current_vg_override = ovr;
    current_vg_node = &ovr->payload;
@@ -1891,6 +1886,13 @@ _vg_close_block(void)
 {
    if (current_vg_override)
      {
+        /* If the override block closed without a target: statement, the
+           placeholder is orphaned (never appended to the description's
+           override list). Emit the missing-target error here so it isn't
+           silently swallowed. */
+        if (!current_vg_override->target_name ||
+            !*current_vg_override->target_name)
+          error_and_abort(NULL, "vector override block missing target: statement");
         current_vg_override = NULL;
         current_vg_node = NULL;
      }
@@ -1959,17 +1961,222 @@ _vg_finalize_description_overrides(void)
      }
 }
 
-/* Task 4.3: Set the target name for the current override block. */
+/* Task 4.4: Merge src's set fields (per field_mask) onto dst.
+ * This is used when a child override block targets the same node as an
+ * inherited override: the child's properties (from the placeholder) are
+ * applied onto the existing inherited entry, and the placeholder is freed.
+ * Uses stringshare_replace so dst properly releases its old references. */
+static void
+_merge_override_fields(Edje_Vg_Override *dst, const Edje_Vg_Override *src)
+{
+   uint64_t m = src->field_mask;
+   if (!m) return;
+
+   dst->field_mask |= m;
+
+   /* Common fields */
+   if (m & EDJE_VG_OVR_VISIBLE)
+     dst->payload.visible = src->payload.visible;
+
+   if (m & EDJE_VG_OVR_COLOR_RAW)
+     {
+        dst->payload.color.r = src->payload.color.r;
+        dst->payload.color.g = src->payload.color.g;
+        dst->payload.color.b = src->payload.color.b;
+        dst->payload.color.a = src->payload.color.a;
+     }
+   if (m & EDJE_VG_OVR_COLOR_CLASS)
+     eina_stringshare_replace(&dst->payload.color.color_class,
+                              src->payload.color.color_class);
+
+   if (m & EDJE_VG_OVR_TRANSFORM)
+     dst->payload.xform = src->payload.xform;
+
+   /* Shape-specific */
+   if (src->expected_type == EDJE_VG_NODE_SHAPE)
+     {
+        if (m & EDJE_VG_OVR_PATH)
+          eina_stringshare_replace(&dst->payload.shape.path,
+                                   src->payload.shape.path);
+
+        if (m & EDJE_VG_OVR_FILL_RAW)
+          {
+             dst->payload.shape.fill.r = src->payload.shape.fill.r;
+             dst->payload.shape.fill.g = src->payload.shape.fill.g;
+             dst->payload.shape.fill.b = src->payload.shape.fill.b;
+             dst->payload.shape.fill.a = src->payload.shape.fill.a;
+             eina_stringshare_replace(&dst->payload.shape.gradient_ref,
+                                      src->payload.shape.gradient_ref);
+          }
+
+        if (m & EDJE_VG_OVR_FILL_CLASS)
+          eina_stringshare_replace(&dst->payload.shape.fill.color_class,
+                                   src->payload.shape.fill.color_class);
+
+        if (m & EDJE_VG_OVR_STROKE_RAW)
+          {
+             dst->payload.shape.stroke_color.r = src->payload.shape.stroke_color.r;
+             dst->payload.shape.stroke_color.g = src->payload.shape.stroke_color.g;
+             dst->payload.shape.stroke_color.b = src->payload.shape.stroke_color.b;
+             dst->payload.shape.stroke_color.a = src->payload.shape.stroke_color.a;
+          }
+
+        if (m & EDJE_VG_OVR_STROKE_CLASS)
+          eina_stringshare_replace(&dst->payload.shape.stroke_color.color_class,
+                                   src->payload.shape.stroke_color.color_class);
+
+        if (m & EDJE_VG_OVR_STROKE_WIDTH)
+          dst->payload.shape.stroke_width = src->payload.shape.stroke_width;
+
+        if (m & EDJE_VG_OVR_STROKE_DASH)
+          {
+             free(dst->payload.shape.stroke_dash);
+             dst->payload.shape.stroke_dash       = NULL;
+             dst->payload.shape.stroke_dash_count = 0;
+             if (src->payload.shape.stroke_dash_count > 0
+                 && src->payload.shape.stroke_dash)
+               {
+                  size_t sz = src->payload.shape.stroke_dash_count * sizeof(double);
+                  dst->payload.shape.stroke_dash = malloc(sz);
+                  if (dst->payload.shape.stroke_dash)
+                    {
+                       memcpy(dst->payload.shape.stroke_dash,
+                              src->payload.shape.stroke_dash, sz);
+                       dst->payload.shape.stroke_dash_count =
+                          src->payload.shape.stroke_dash_count;
+                    }
+               }
+          }
+
+        if (m & EDJE_VG_OVR_STROKE_CAP)
+          dst->payload.shape.stroke_cap = src->payload.shape.stroke_cap;
+
+        if (m & EDJE_VG_OVR_STROKE_JOIN)
+          dst->payload.shape.stroke_join = src->payload.shape.stroke_join;
+
+        if (m & EDJE_VG_OVR_FILL_RULE)
+          dst->payload.shape.fill_rule = src->payload.shape.fill_rule;
+     }
+
+   /* Gradient-specific */
+   if (src->expected_type == EDJE_VG_NODE_GRADIENT_LINEAR ||
+       src->expected_type == EDJE_VG_NODE_GRADIENT_RADIAL)
+     {
+        if (m & EDJE_VG_OVR_GRAD_GEOM)
+          {
+             dst->payload.gradient.x0     = src->payload.gradient.x0;
+             dst->payload.gradient.y0     = src->payload.gradient.y0;
+             dst->payload.gradient.x1     = src->payload.gradient.x1;
+             dst->payload.gradient.y1     = src->payload.gradient.y1;
+             dst->payload.gradient.radius = src->payload.gradient.radius;
+          }
+
+        if (m & EDJE_VG_OVR_GRAD_SPREAD)
+          dst->payload.gradient.spread = src->payload.gradient.spread;
+
+        if (m & EDJE_VG_OVR_GRAD_STOPS)
+          {
+             /* Free dst's old stops. */
+             if (dst->payload.gradient.stops)
+               {
+                  unsigned int i;
+                  for (i = 0; i < dst->payload.gradient.stops_count; i++)
+                    {
+                       if (dst->payload.gradient.stops[i].color.color_class)
+                         {
+                            eina_stringshare_del(
+                               dst->payload.gradient.stops[i].color.color_class);
+                            dst->payload.gradient.stops[i].color.color_class = NULL;
+                         }
+                    }
+                  free(dst->payload.gradient.stops);
+                  dst->payload.gradient.stops       = NULL;
+                  dst->payload.gradient.stops_count = 0;
+               }
+             /* Copy src's stops. */
+             if (src->payload.gradient.stops_count > 0
+                 && src->payload.gradient.stops)
+               {
+                  size_t sz = src->payload.gradient.stops_count
+                                 * sizeof(Edje_Vg_Stop);
+                  dst->payload.gradient.stops = malloc(sz);
+                  if (dst->payload.gradient.stops)
+                    {
+                       unsigned int i;
+                       memcpy(dst->payload.gradient.stops,
+                              src->payload.gradient.stops, sz);
+                       dst->payload.gradient.stops_count =
+                          src->payload.gradient.stops_count;
+                       for (i = 0; i < dst->payload.gradient.stops_count; i++)
+                         {
+                            const char *cc =
+                               dst->payload.gradient.stops[i].color.color_class;
+                            if (cc)
+                              dst->payload.gradient.stops[i].color.color_class =
+                                 eina_stringshare_ref(cc);
+                         }
+                    }
+               }
+          }
+     }
+}
+
+/* Task 4.4: Set the target name for the current override block, and decide
+ * whether to merge with an existing inherited entry or append a new one.
+ *
+ * At opener time (_vg_open_override) we couldn't check because target_name
+ * wasn't known yet.  Now that target: has fired we can walk the description's
+ * existing overrides (which include any deep-copied inherited ones) and merge
+ * if a matching (target_name, expected_type) pair is found. */
 static void
 st_vg_override_target(void)
 {
    const char *s;
+   Edje_Part_Description_Vector *ed;
+   Eina_List *l;
+   Edje_Vg_Override *existing;
 
    check_arg_count(1);
    if (!current_vg_override)
      error_and_abort(NULL, "target: outside vector override block");
+
    s = parse_str(0);
+   ed = (Edje_Part_Description_Vector *)current_desc;
+
+   /* Walk existing overrides for a matching (target_name, expected_type). */
+   EINA_LIST_FOREACH(ed->vg.overrides, l, existing)
+     {
+        if (existing->expected_type == current_vg_override->expected_type
+            && existing->target_name
+            && !strcmp(existing->target_name, s))
+          {
+             /* Merge any fields already set on the placeholder onto the
+              * existing entry (option b from the plan: robust against
+              * authors writing properties before target:).
+              * _merge_override_fields refs dst's new strings independently;
+              * the placeholder's own string refs (path, color_class, etc.)
+              * remain owned by the placeholder and must be released. */
+             if (current_vg_override->field_mask)
+               _merge_override_fields(existing, current_vg_override);
+
+             /* Discard placeholder, releasing any stringshare refs it holds
+              * in its payload (accumulated if property statements appeared
+              * before target:).  target_name is still NULL at this point. */
+             _edje_vg_override_free(current_vg_override);
+
+             /* Route subsequent property statements in this block into the
+              * existing (merged) entry. */
+             current_vg_override = existing;
+             current_vg_node     = &existing->payload;
+             free((char *)s);
+             return;
+          }
+     }
+
+   /* No existing match — this is a fresh override; set target_name and
+    * append to the description's list. */
    eina_stringshare_replace(&current_vg_override->target_name, s);
+   ed->vg.overrides = eina_list_append(ed->vg.overrides, current_vg_override);
    free((char *)s);
 }
 
@@ -10037,6 +10244,28 @@ st_collections_group_parts_part_description_inherit(void)
                    eina_hash_set(_vg_desc_to_tree, &ied, parent_tree);
                 }
            }
+
+         /* Task 4.4: deep-copy parent's override list onto the child.
+          * The *ed = *parent shallow-copy above copied the pointer value for
+          * vg.overrides; we must replace it with a fresh deep-copied list so
+          * that child can later merge its own override blocks without mutating
+          * the parent's entries.
+          *
+          * After the shallow copy, ied->vg.overrides == iparent->vg.overrides.
+          * Reset child's list first, then deep-copy each entry. */
+         ied->vg.overrides = NULL;
+         if (iparent->vg.overrides)
+           {
+              Eina_List *ol;
+              Edje_Vg_Override *p_ovr;
+
+              EINA_LIST_FOREACH(iparent->vg.overrides, ol, p_ovr)
+                {
+                   Edje_Vg_Override *c_ovr = _edje_vg_override_dup(p_ovr);
+                   if (c_ovr)
+                     ied->vg.overrides = eina_list_append(ied->vg.overrides, c_ovr);
+                }
+           }
          break;
       }
      }
diff --git a/src/lib/edje/edje_vg_tree.c b/src/lib/edje/edje_vg_tree.c
index 296ee60f95..9ee8850d57 100644
--- a/src/lib/edje/edje_vg_tree.c
+++ b/src/lib/edje/edje_vg_tree.c
@@ -738,6 +738,235 @@ _edje_vg_tree_apply_override(Edje_Vg_Tree *working,
      }
 }
 
+/* =========================================================================
+ * Phase 4.4 — Override dup + free helpers
+ *
+ * _edje_vg_override_dup: deep-copy a single Edje_Vg_Override.  All heap
+ * allocations inside the payload are independently owned by the duplicate.
+ *
+ * _edje_vg_override_free: release all heap allocations inside an override
+ * (stringshares + arrays) and free(ovr) itself.
+ * ========================================================================= */
+
+EAPI Edje_Vg_Override *
+_edje_vg_override_dup(const Edje_Vg_Override *src)
+{
+   Edje_Vg_Override *dup;
+
+   if (!src) return NULL;
+
+   dup = calloc(1, sizeof(Edje_Vg_Override));
+   if (!dup) return NULL;
+
+   dup->target_name   = src->target_name
+                           ? eina_stringshare_ref(src->target_name) : NULL;
+   dup->expected_type = src->expected_type;
+   dup->field_mask    = src->field_mask;
+
+   /* ---- Common payload fields ---- */
+   if (src->field_mask & EDJE_VG_OVR_VISIBLE)
+     dup->payload.visible = src->payload.visible;
+
+   if (src->field_mask & EDJE_VG_OVR_COLOR_RAW)
+     {
+        dup->payload.color.r = src->payload.color.r;
+        dup->payload.color.g = src->payload.color.g;
+        dup->payload.color.b = src->payload.color.b;
+        dup->payload.color.a = src->payload.color.a;
+     }
+   if (src->field_mask & EDJE_VG_OVR_COLOR_CLASS)
+     dup->payload.color.color_class = src->payload.color.color_class
+                                       ? eina_stringshare_ref(src->payload.color.color_class)
+                                       : NULL;
+
+   if (src->field_mask & EDJE_VG_OVR_TRANSFORM)
+     dup->payload.xform = src->payload.xform;
+
+   /* ---- Shape-specific payload fields ---- */
+   if (src->expected_type == EDJE_VG_NODE_SHAPE)
+     {
+        if (src->field_mask & EDJE_VG_OVR_PATH)
+          dup->payload.shape.path = src->payload.shape.path
+                                     ? eina_stringshare_ref(src->payload.shape.path)
+                                     : NULL;
+
+        if (src->field_mask & EDJE_VG_OVR_FILL_RAW)
+          {
+             dup->payload.shape.fill.r = src->payload.shape.fill.r;
+             dup->payload.shape.fill.g = src->payload.shape.fill.g;
+             dup->payload.shape.fill.b = src->payload.shape.fill.b;
+             dup->payload.shape.fill.a = src->payload.shape.fill.a;
+             dup->payload.shape.gradient_ref =
+                src->payload.shape.gradient_ref
+                   ? eina_stringshare_ref(src->payload.shape.gradient_ref)
+                   : NULL;
+          }
+
+        if (src->field_mask & EDJE_VG_OVR_FILL_CLASS)
+          dup->payload.shape.fill.color_class =
+             src->payload.shape.fill.color_class
+                ? eina_stringshare_ref(src->payload.shape.fill.color_class)
+                : NULL;
+
+        if (src->field_mask & EDJE_VG_OVR_STROKE_RAW)
+          {
+             dup->payload.shape.stroke_color.r = src->payload.shape.stroke_color.r;
+             dup->payload.shape.stroke_color.g = src->payload.shape.stroke_color.g;
+             dup->payload.shape.stroke_color.b = src->payload.shape.stroke_color.b;
+             dup->payload.shape.stroke_color.a = src->payload.shape.stroke_color.a;
+          }
+
+        if (src->field_mask & EDJE_VG_OVR_STROKE_CLASS)
+          dup->payload.shape.stroke_color.color_class =
+             src->payload.shape.stroke_color.color_class
+                ? eina_stringshare_ref(src->payload.shape.stroke_color.color_class)
+                : NULL;
+
+        if (src->field_mask & EDJE_VG_OVR_STROKE_WIDTH)
+          dup->payload.shape.stroke_width = src->payload.shape.stroke_width;
+
+        if (src->field_mask & EDJE_VG_OVR_STROKE_DASH)
+          {
+             if (src->payload.shape.stroke_dash_count > 0
+                 && src->payload.shape.stroke_dash)
+               {
+                  size_t sz = src->payload.shape.stroke_dash_count * sizeof(double);
+                  dup->payload.shape.stroke_dash = malloc(sz);
+                  if (dup->payload.shape.stroke_dash)
+                    {
+                       memcpy(dup->payload.shape.stroke_dash,
+                              src->payload.shape.stroke_dash, sz);
+                       dup->payload.shape.stroke_dash_count =
+                          src->payload.shape.stroke_dash_count;
+                    }
+               }
+          }
+
+        if (src->field_mask & EDJE_VG_OVR_STROKE_CAP)
+          dup->payload.shape.stroke_cap = src->payload.shape.stroke_cap;
+
+        if (src->field_mask & EDJE_VG_OVR_STROKE_JOIN)
+          dup->payload.shape.stroke_join = src->payload.shape.stroke_join;
+
+        if (src->field_mask & EDJE_VG_OVR_FILL_RULE)
+          dup->payload.shape.fill_rule = src->payload.shape.fill_rule;
+     }
+
+   /* ---- Gradient-specific payload fields ---- */
+   if (src->expected_type == EDJE_VG_NODE_GRADIENT_LINEAR ||
+       src->expected_type == EDJE_VG_NODE_GRADIENT_RADIAL)
+     {
+        if (src->field_mask & EDJE_VG_OVR_GRAD_GEOM)
+          {
+             dup->payload.gradient.x0     = src->payload.gradient.x0;
+             dup->payload.gradient.y0     = src->payload.gradient.y0;
+             dup->payload.gradient.x1     = src->payload.gradient.x1;
+             dup->payload.gradient.y1     = src->payload.gradient.y1;
+             dup->payload.gradient.radius = src->payload.gradient.radius;
+          }
+
+        if (src->field_mask & EDJE_VG_OVR_GRAD_SPREAD)
+          dup->payload.gradient.spread = src->payload.gradient.spread;
+
+        if (src->field_mask & EDJE_VG_OVR_GRAD_STOPS
+            && src->payload.gradient.stops_count > 0
+            && src->payload.gradient.stops)
+          {
+             size_t sz = src->payload.gradient.stops_count * sizeof(Edje_Vg_Stop);
+             dup->payload.gradient.stops = malloc(sz);
+             if (dup->payload.gradient.stops)
+               {
+                  unsigned int i;
+                  memcpy(dup->payload.gradient.stops,
+                         src->payload.gradient.stops, sz);
+                  dup->payload.gradient.stops_count =
+                     src->payload.gradient.stops_count;
+                  for (i = 0; i < dup->payload.gradient.stops_count; i++)
+                    {
+                       const char *cc =
+                          dup->payload.gradient.stops[i].color.color_class;
+                       if (cc)
+                          dup->payload.gradient.stops[i].color.color_class =
+                             eina_stringshare_ref(cc);
+                    }
+               }
+          }
+     }
+
+   return dup;
+}
+
+EAPI void
+_edje_vg_override_free(Edje_Vg_Override *ovr)
+{
+   if (!ovr) return;
+
+   if (ovr->target_name)
+     {
+        eina_stringshare_del(ovr->target_name);
+        ovr->target_name = NULL;
+     }
+
+   /* Common color_class */
+   if (ovr->payload.color.color_class)
+     {
+        eina_stringshare_del(ovr->payload.color.color_class);
+        ovr->payload.color.color_class = NULL;
+     }
+
+   /* Shape-specific */
+   if (ovr->expected_type == EDJE_VG_NODE_SHAPE)
+     {
+        if (ovr->payload.shape.path)
+          {
+             eina_stringshare_del(ovr->payload.shape.path);
+             ovr->payload.shape.path = NULL;
+          }
+        if (ovr->payload.shape.fill.color_class)
+          {
+             eina_stringshare_del(ovr->payload.shape.fill.color_class);
+             ovr->payload.shape.fill.color_class = NULL;
+          }
+        if (ovr->payload.shape.gradient_ref)
+          {
+             eina_stringshare_del(ovr->payload.shape.gradient_ref);
+             ovr->payload.shape.gradient_ref = NULL;
+          }
+        if (ovr->payload.shape.stroke_color.color_class)
+          {
+             eina_stringshare_del(ovr->payload.shape.stroke_color.color_class);
+             ovr->payload.shape.stroke_color.color_class = NULL;
+          }
+        free(ovr->payload.shape.stroke_dash);
+        ovr->payload.shape.stroke_dash       = NULL;
+        ovr->payload.shape.stroke_dash_count = 0;
+     }
+
+   /* Gradient-specific */
+   if (ovr->expected_type == EDJE_VG_NODE_GRADIENT_LINEAR ||
+       ovr->expected_type == EDJE_VG_NODE_GRADIENT_RADIAL)
+     {
+        if (ovr->payload.gradient.stops)
+          {
+             unsigned int i;
+             for (i = 0; i < ovr->payload.gradient.stops_count; i++)
+               {
+                  if (ovr->payload.gradient.stops[i].color.color_class)
+                    {
+                       eina_stringshare_del(
+                          ovr->payload.gradient.stops[i].color.color_class);
+                       ovr->payload.gradient.stops[i].color.color_class = NULL;
+                    }
+               }
+             free(ovr->payload.gradient.stops);
+             ovr->payload.gradient.stops       = NULL;
+             ovr->payload.gradient.stops_count = 0;
+          }
+     }
+
+   free(ovr);
+}
+
 /* =========================================================================
  * Phase 3.1 — Materializer: Edje_Vg_Tree → Efl_VG object tree
  *
diff --git a/src/lib/edje/edje_vg_tree.h b/src/lib/edje/edje_vg_tree.h
index 4f56404042..3e30a199c3 100644
--- a/src/lib/edje/edje_vg_tree.h
+++ b/src/lib/edje/edje_vg_tree.h
@@ -84,6 +84,19 @@ EAPI Edje_Vg_Tree *_edje_vg_tree_dup(const Edje_Vg_Tree *t);
 EAPI void _edje_vg_tree_apply_override(Edje_Vg_Tree *working,
                                        const Edje_Vg_Override *ovr);
 
+/* --- Phase 4.4: Override dup + free helpers ------------------------------ */
+
+/* Deep-copy a single Edje_Vg_Override.  All heap allocations in the
+ * payload (stringshares, arrays) are independently owned by the duplicate.
+ * Stringshares are ref'd rather than re-interned.  Returns NULL on OOM or
+ * when src is NULL. */
+EAPI Edje_Vg_Override *_edje_vg_override_dup(const Edje_Vg_Override *src);
+
+/* Release all heap allocations inside an override (stringshares, dash
+ * arrays, gradient stop arrays) and free(ovr) itself.
+ * Safe to call with NULL. */
+EAPI void              _edje_vg_override_free(Edje_Vg_Override *ovr);
+
 /* --- Eet descriptor accessor -------------------------------------------- */
 
 /* Returns the static Eet_Data_Descriptor for Edje_Vg_Tree.
diff --git a/src/tests/edje/data/test_vector_states.edc b/src/tests/edje/data/test_vector_states.edc
index 69617c45d0..1e1e4288de 100644
--- a/src/tests/edje/data/test_vector_states.edc
+++ b/src/tests/edje/data/test_vector_states.edc
@@ -57,4 +57,28 @@ collections {
          }
       } }
    }
+   /* Task 4.4: inherit + per-target merge fixture.
+    * The default description sets visible and stroke.width on "body".
+    * The active description inherits from default (getting those fields) and
+    * then merges a fill.color override on the same "body" target.
+    * Result: child override has VISIBLE | STROKE_WIDTH | FILL_RAW all set. */
+   group { name: "ui/box_inherit_merge";
+      parts { part { name: "vg"; type: VECTOR;
+         description { state: "default" 0.0;
+            vector.use: "tile_a";
+            vector.shape {
+               target: "body";
+               visible: 1;
+               stroke.width: 2;
+            }
+         }
+         description { state: "active" 0.0;
+            inherit: "default" 0.0;
+            vector.shape {
+               target: "body";
+               fill.color: 200 200 0 255;
+            }
+         }
+      } }
+   }
 }

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to