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(¤t_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.