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 90e64ede1efb338f7b1424df9857b8b37684dfe4
Author: [email protected] <[email protected]>
AuthorDate: Tue Apr 28 11:50:42 2026 -0600
edje: expand edje_vg_tree helper API for Phase 2 parser
Phase 2 requires a fuller tree-building substrate than the Phase-1
test fixture provides. Add six internal helpers:
- _edje_vg_tree_new() allocates tree with root container
- _edje_vg_node_new(type) creates nodes with sensible defaults
- _edje_vg_container_append() adds children to containers
- _edje_vg_tree_node_by_name() enables shape override resolution
- _edje_vg_tree_validate_unique_names() detects duplicate VEC001 errors
- _edje_vg_node_free() is now public (previously _node_free)
Consolidate all node freeing into _edje_vg_node_free to avoid
redundant logic. Replace the manual EINA_LIST_FOREACH/free pattern
with EINA_LIST_FREE for simplicity. The Phase-1 round-trip test suite
still passes (44/44).
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
---
src/lib/edje/edje_vg_tree.c | 119 ++++++++++++++++++++++++++++---
src/lib/edje/edje_vg_tree.h | 30 ++++++++
src/tests/edje/edje_test_vector_states.c | 95 ++++++++++++++++++++++++
3 files changed, 234 insertions(+), 10 deletions(-)
diff --git a/src/lib/edje/edje_vg_tree.c b/src/lib/edje/edje_vg_tree.c
index 489dd1e467..f535d15a42 100644
--- a/src/lib/edje/edje_vg_tree.c
+++ b/src/lib/edje/edje_vg_tree.c
@@ -134,8 +134,6 @@ _edje_vg_tree_new_for_test(void)
* _edje_vg_tree_free — recursive free
* ========================================================================= */
-static void _node_free(Edje_Vg_Node *n);
-
static void
_color_binding_free_fields(Edje_Vg_Color_Binding *cb)
{
@@ -146,8 +144,8 @@ _color_binding_free_fields(Edje_Vg_Color_Binding *cb)
}
}
-static void
-_node_free(Edje_Vg_Node *n)
+EAPI void
+_edje_vg_node_free(Edje_Vg_Node *n)
{
if (!n) return;
@@ -173,12 +171,9 @@ _node_free(Edje_Vg_Node *n)
case EDJE_VG_NODE_CONTAINER:
{
- Eina_List *l;
Edje_Vg_Node *child;
- EINA_LIST_FOREACH(n->container.children, l, child)
- _node_free(child);
- eina_list_free(n->container.children);
- n->container.children = NULL;
+ EINA_LIST_FREE(n->container.children, child)
+ _edje_vg_node_free(child);
}
break;
@@ -205,7 +200,7 @@ EAPI void
_edje_vg_tree_free_fields(Edje_Vg_Tree *t)
{
if (!t) return;
- _node_free(t->root);
+ _edje_vg_node_free(t->root);
t->root = NULL;
}
@@ -217,6 +212,110 @@ _edje_vg_tree_free(Edje_Vg_Tree *t)
free(t);
}
+/* =========================================================================
+ * Phase 2 construction helpers
+ * ========================================================================= */
+
+EAPI Edje_Vg_Tree *
+_edje_vg_tree_new(void)
+{
+ Edje_Vg_Tree *t = calloc(1, sizeof(Edje_Vg_Tree));
+ if (!t) return NULL;
+ t->root = _edje_vg_node_new(EDJE_VG_NODE_CONTAINER);
+ if (!t->root) { free(t); return NULL; }
+ return t;
+}
+
+EAPI Edje_Vg_Node *
+_edje_vg_node_new(Edje_Vg_Node_Type type)
+{
+ Edje_Vg_Node *n = calloc(1, sizeof(Edje_Vg_Node));
+ if (!n) return NULL;
+ n->type = type;
+ n->visible = 1;
+ n->color.r = n->color.g = n->color.b = n->color.a = 255;
+ n->xform.sx = n->xform.sy = 1.0; /* identity scale */
+ if (type == EDJE_VG_NODE_SHAPE)
+ {
+ n->shape.fill.r = n->shape.fill.g =
+ n->shape.fill.b = n->shape.fill.a = 255;
+ n->shape.stroke_width = 1.0;
+ n->shape.stroke_cap = EFL_GFX_CAP_BUTT;
+ n->shape.stroke_join = EFL_GFX_JOIN_MITER;
+ n->shape.fill_rule = EFL_GFX_FILL_RULE_WINDING;
+ }
+ return n;
+}
+
+EAPI void
+_edje_vg_container_append(Edje_Vg_Node *parent, Edje_Vg_Node *child)
+{
+ if (!parent || parent->type != EDJE_VG_NODE_CONTAINER || !child) return;
+ parent->container.children =
+ eina_list_append(parent->container.children, child);
+}
+
+static Edje_Vg_Node *
+_node_by_name_recursive(Edje_Vg_Node *n, const char *name)
+{
+ if (!n || !name) return NULL;
+ if (n->name && !strcmp(n->name, name)) return n;
+ if (n->type == EDJE_VG_NODE_CONTAINER)
+ {
+ Eina_List *l;
+ Edje_Vg_Node *c;
+ EINA_LIST_FOREACH(n->container.children, l, c)
+ {
+ Edje_Vg_Node *r = _node_by_name_recursive(c, name);
+ if (r) return r;
+ }
+ }
+ return NULL;
+}
+
+EAPI Edje_Vg_Node *
+_edje_vg_tree_node_by_name(const Edje_Vg_Tree *t, const char *name)
+{
+ if (!t || !t->root) return NULL;
+ return _node_by_name_recursive(t->root, name);
+}
+
+static Eina_Bool
+_validate_unique_recursive(Edje_Vg_Node *n, Eina_Hash *seen,
+ const char **dup_out)
+{
+ if (n->name)
+ {
+ if (eina_hash_find(seen, n->name))
+ {
+ if (dup_out) *dup_out = n->name;
+ return EINA_FALSE;
+ }
+ eina_hash_add(seen, n->name, n);
+ }
+ if (n->type == EDJE_VG_NODE_CONTAINER)
+ {
+ Eina_List *l;
+ Edje_Vg_Node *c;
+ EINA_LIST_FOREACH(n->container.children, l, c)
+ if (!_validate_unique_recursive(c, seen, dup_out))
+ return EINA_FALSE;
+ }
+ return EINA_TRUE;
+}
+
+EAPI Eina_Bool
+_edje_vg_tree_validate_unique_names(const Edje_Vg_Tree *t,
+ const char **dup_out)
+{
+ if (!t || !t->root) return EINA_TRUE;
+ Eina_Hash *seen = eina_hash_string_superfast_new(NULL);
+ if (!seen) return EINA_TRUE; /* OOM: fail open rather than crash */
+ Eina_Bool ok = _validate_unique_recursive(t->root, seen, dup_out);
+ eina_hash_free(seen);
+ return ok;
+}
+
/* =========================================================================
* _edje_vg_tree_equal — recursive structural equality
* ========================================================================= */
diff --git a/src/lib/edje/edje_vg_tree.h b/src/lib/edje/edje_vg_tree.h
index 97666970f9..0a4877bf37 100644
--- a/src/lib/edje/edje_vg_tree.h
+++ b/src/lib/edje/edje_vg_tree.h
@@ -28,6 +28,36 @@ EAPI void _edje_vg_tree_free_fields(Edje_Vg_Tree *t);
/* Recursive free. Safe to call with NULL. */
EAPI void _edje_vg_tree_free(Edje_Vg_Tree *t);
+/* --- Construction helpers (Phase 2+) ------------------------------------ */
+
+/* Allocate a new, empty tree with a CONTAINER root. */
+EAPI Edje_Vg_Tree *_edje_vg_tree_new(void);
+
+/* Allocate a node of the given type with sane default property values.
+ * color RGBA = 255 white, scale = 1.0 identity.
+ * SHAPE nodes additionally get: stroke_width=1.0, stroke_cap=BUTT,
+ * stroke_join=MITER, fill_rule=WINDING, fill RGBA = 255 white. */
+EAPI Edje_Vg_Node *_edje_vg_node_new(Edje_Vg_Node_Type type);
+
+/* Recursive free. Safe to call with NULL. */
+EAPI void _edje_vg_node_free(Edje_Vg_Node *n);
+
+/* Append child to parent (parent must be CONTAINER). No-op on bad args. */
+EAPI void _edje_vg_container_append(Edje_Vg_Node *parent,
+ Edje_Vg_Node *child);
+
+/* Depth-first search for first node whose name equals name. Returns NULL
+ * if not found. */
+EAPI Edje_Vg_Node *_edje_vg_tree_node_by_name(const Edje_Vg_Tree *t,
+ const char *name);
+
+/* Walk the entire tree and verify every named node has a unique name.
+ * Returns EINA_TRUE if all names are unique. On first collision, returns
+ * EINA_FALSE and sets *dup_out to the duplicate name (stringshare pointer
+ * owned by the node; do NOT free). dup_out may be NULL. */
+EAPI Eina_Bool _edje_vg_tree_validate_unique_names(const Edje_Vg_Tree *t,
+ const char **dup_out);
+
/* --- Eet descriptor accessor -------------------------------------------- */
/* Returns the static Eet_Data_Descriptor for Edje_Vg_Tree.
diff --git a/src/tests/edje/edje_test_vector_states.c b/src/tests/edje/edje_test_vector_states.c
index 6c9a2d7d21..70a3ffcfe1 100644
--- a/src/tests/edje/edje_test_vector_states.c
+++ b/src/tests/edje/edje_test_vector_states.c
@@ -17,13 +17,39 @@
* edje_private.h. Redeclare as incomplete struct so the compiler accepts
* the pointer parameters without knowing the layout. */
typedef struct _Edje_Vg_Tree Edje_Vg_Tree;
+typedef struct _Edje_Vg_Node Edje_Vg_Node;
+typedef enum {
+ EDJE_VG_NODE_SHAPE = 0,
+ EDJE_VG_NODE_CONTAINER = 1,
+ EDJE_VG_NODE_GRADIENT_LINEAR = 2,
+ EDJE_VG_NODE_GRADIENT_RADIAL = 3,
+} Edje_Vg_Node_Type;
+
+/* name and type are the first two fields of _Edje_Vg_Node (edje_private.h).
+ * A partial view struct is safe for accessing only those fields.
+ * DO NOT access any other fields through this view. */
+struct _Edje_Vg_Node { const char *name; Edje_Vg_Node_Type type; /* opaque tail */ };
+struct _Edje_Vg_Tree { double vbx, vby, vbw, vbh; Edje_Vg_Node *root; };
+
+/* Phase 1 helpers */
Edje_Vg_Tree *_edje_vg_tree_new_for_test(void);
Eina_Bool _edje_vg_tree_equal(const Edje_Vg_Tree *a,
const Edje_Vg_Tree *b);
void _edje_vg_tree_free(Edje_Vg_Tree *t);
Eet_Data_Descriptor *_edje_vg_tree_edd(void);
+/* Phase 2 helpers (Task 2.1) */
+Edje_Vg_Tree *_edje_vg_tree_new(void);
+Edje_Vg_Node *_edje_vg_node_new(Edje_Vg_Node_Type type);
+void _edje_vg_node_free(Edje_Vg_Node *n);
+void _edje_vg_container_append(Edje_Vg_Node *parent,
+ Edje_Vg_Node *child);
+Edje_Vg_Node *_edje_vg_tree_node_by_name(const Edje_Vg_Tree *t,
+ const char *name);
+Eina_Bool _edje_vg_tree_validate_unique_names(const Edje_Vg_Tree *t,
+ const char **dup_out);
+
/*
* edje_vg_tree_eet_roundtrip
*
@@ -65,8 +91,77 @@ EFL_START_TEST(edje_vg_tree_eet_roundtrip)
}
EFL_END_TEST
+/*
+ * edje_vg_tree_helpers_basic
+ *
+ * Unit test for the six Phase 2.1 construction helpers:
+ * _edje_vg_tree_new, _edje_vg_node_new, _edje_vg_node_free,
+ * _edje_vg_container_append, _edje_vg_tree_node_by_name,
+ * _edje_vg_tree_validate_unique_names.
+ *
+ * The test accesses only the first two fields of Edje_Vg_Node (name, type)
+ * which are layout-stable at the head of the struct. Edje_Vg_Tree.root is
+ * the last field after four doubles — accessed via the partial view struct
+ * defined above.
+ */
+EFL_START_TEST(edje_vg_tree_helpers_basic)
+{
+ /* _edje_vg_tree_new: root container auto-created. */
+ Edje_Vg_Tree *t = _edje_vg_tree_new();
+ fail_if(!t);
+ fail_if(t->root == NULL);
+ fail_if(t->root->type != EDJE_VG_NODE_CONTAINER);
+
+ /* _edje_vg_node_new: shape node with correct type. */
+ Edje_Vg_Node *s = _edje_vg_node_new(EDJE_VG_NODE_SHAPE);
+ fail_if(!s);
+ fail_if(s->type != EDJE_VG_NODE_SHAPE);
+
+ /* Set name (first field) — mirrors how the parser will set it. */
+ s->name = eina_stringshare_add("body");
+
+ /* _edje_vg_container_append: append to root, then look up by name. */
+ _edje_vg_container_append(t->root, s);
+ Edje_Vg_Node *found = _edje_vg_tree_node_by_name(t, "body");
+ fail_if(found != s);
+ fail_if(_edje_vg_tree_node_by_name(t, "missing") != NULL);
+ fail_if(_edje_vg_tree_node_by_name(NULL, "body") != NULL);
+
+ /* _edje_vg_tree_validate_unique_names: one unique name — passes. */
+ const char *dup = NULL;
+ fail_if(!_edje_vg_tree_validate_unique_names(t, &dup));
+ fail_if(dup != NULL);
+
+ /* Duplicate detection: add a second "body" node. */
+ Edje_Vg_Node *s2 = _edje_vg_node_new(EDJE_VG_NODE_SHAPE);
+ fail_if(!s2);
+ s2->name = eina_stringshare_add("body");
+ _edje_vg_container_append(t->root, s2);
+ fail_if(_edje_vg_tree_validate_unique_names(t, &dup));
+ fail_if(dup == NULL || strcmp(dup, "body") != 0);
+
+ /* _edje_vg_container_append: no-op on bad args — must not crash. */
+ _edje_vg_container_append(NULL, s2);
+ _edje_vg_container_append(s2, NULL); /* s2 is SHAPE, not CONTAINER */
+ _edje_vg_container_append(s2, s); /* s2 is SHAPE — append rejected */
+
+ /* _edje_vg_tree_free: recursively frees s and s2 (both owned by root). */
+ _edje_vg_tree_free(t);
+
+ /* _edje_vg_node_free: NULL-safe. */
+ _edje_vg_node_free(NULL);
+
+ /* _edje_vg_node_new CONTAINER: correct type. */
+ Edje_Vg_Node *c = _edje_vg_node_new(EDJE_VG_NODE_CONTAINER);
+ fail_if(!c);
+ fail_if(c->type != EDJE_VG_NODE_CONTAINER);
+ _edje_vg_node_free(c);
+}
+EFL_END_TEST
+
void
edje_test_vector_states(TCase *tc)
{
tcase_add_test(tc, edje_vg_tree_eet_roundtrip);
+ tcase_add_test(tc, edje_vg_tree_helpers_basic);
}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.