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 4003bc4373f41666dfbb81baca28af7390776b6c
Author: [email protected] <[email protected]>
AuthorDate: Wed Apr 29 14:50:46 2026 -0600

    edje: register per-part color-class observers for vector descriptions (Task 6.1)
    
    This patch extends color-class observer registration to VECTOR descriptions,
    matching the long-standing behavior of RECT, IMAGE, and TEXT parts. Each
    description now registers its color_class bindings with _edje_color_class_member.
    
    Two new tree-walk helpers in edje_vg_tree.c inspect the base tree and override
    list: _edje_vg_tree_for_each_class_name emits all non-empty color_class
    references (node-level, shape fill/stroke, gradient stops), and
    _edje_vg_overrides_for_each_class_name filters overrides by field_mask to emit
    only touched bindings. Registration happens in _edje_process_colorclass;
    symmetric deregistration in _edje_color_class_on_del uses _edje_vg_color_class_on_del
    to walk with observer-del callbacks. Eina's observable ref-counts observers, so
    each add matches exactly one del regardless of class-name duplicates.
    
    The test fixture adds vg_accent and vg_outline color classes, a tile_classed
    vector part, and ui/box_classed group. New test edje_vg_class_member_registered
    loads the group and calls edje_object_color_class_set, confirming no crash. Full
    retint-and-render validation defers to Task 6.2 (resolution + observer-callback
    wiring) and Task 6.4 (materializer fill-vs-node-color fix).
    
    65/65 tests pass. No new leaks under valgrind.
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
---
 src/lib/edje/edje_load.c                   | 79 ++++++++++++++++++++++++++++
 src/lib/edje/edje_private.h                |  1 +
 src/lib/edje/edje_util.c                   |  5 ++
 src/lib/edje/edje_vg_tree.c                | 83 ++++++++++++++++++++++++++++++
 src/lib/edje/edje_vg_tree.h                | 22 ++++++++
 src/tests/edje/data/test_vector_states.edc | 21 ++++++++
 src/tests/edje/edje_test_vector_states.c   | 83 +++++++++++++++++++++++++++++-
 7 files changed, 292 insertions(+), 2 deletions(-)

diff --git a/src/lib/edje/edje_load.c b/src/lib/edje/edje_load.c
index 83aa87daff..cfb16fb7ec 100644
--- a/src/lib/edje/edje_load.c
+++ b/src/lib/edje/edje_load.c
@@ -734,6 +734,72 @@ _edje_devices_add(Edje *ed, Evas *tev)
      efl_event_callback_array_add(tev, edje_device_callbacks(), ed);
 }
 
+/* --- Phase 6.1 helpers: VECTOR description class registration ------------ */
+
+static void
+_vg_register_class_cb(const char *name, void *data)
+{
+   Edje *ed = data;
+   /* efl_observable_observer_add is ref-counted: each add increments the
+    * refcount, each del decrements it. We do not deduplicate here because
+    * _vg_unregister_desc_classes performs the identical walk, so every add
+    * is matched by exactly one del regardless of class-name duplicates. */
+   efl_observable_observer_add(_edje_color_class_member, name, ed->obj);
+}
+
+static void
+_vg_register_desc_classes(Edje *ed, Edje_Part_Description_Vector *vd)
+{
+   const Edje_Vg_Tree *tree;
+
+   if (vd->vg.tree_id < 0) return;
+   if ((unsigned int)vd->vg.tree_id >= ed->file->vector_dir->trees_count) return;
+
+   tree = &ed->file->vector_dir->trees[vd->vg.tree_id];
+   _edje_vg_tree_for_each_class_name(tree, _vg_register_class_cb, ed);
+   _edje_vg_overrides_for_each_class_name(vd->vg.overrides, _vg_register_class_cb, ed);
+}
+
+static void
+_vg_unregister_class_cb(const char *name, void *data)
+{
+   Edje *ed = data;
+   efl_observable_observer_del(_edje_color_class_member, name, ed->obj);
+}
+
+static void
+_vg_unregister_desc_classes(Edje *ed, Edje_Part_Description_Vector *vd)
+{
+   const Edje_Vg_Tree *tree;
+
+   if (vd->vg.tree_id < 0) return;
+   if ((unsigned int)vd->vg.tree_id >= ed->file->vector_dir->trees_count) return;
+
+   tree = &ed->file->vector_dir->trees[vd->vg.tree_id];
+   _edje_vg_tree_for_each_class_name(tree, _vg_unregister_class_cb, ed);
+   _edje_vg_overrides_for_each_class_name(vd->vg.overrides, _vg_unregister_class_cb, ed);
+}
+
+/* Symmetric deregistration: called from _edje_color_class_on_del for each
+ * VECTOR part, mirroring the _vg_register_desc_classes registration above. */
+void
+_edje_vg_color_class_on_del(Edje *ed, Edje_Part *ep)
+{
+   unsigned int k;
+
+   if (ep->type != EDJE_PART_TYPE_VECTOR) return;
+   if (!ed->file || !ed->file->vector_dir) return;
+
+   _vg_unregister_desc_classes(ed,
+      (Edje_Part_Description_Vector *)ep->default_desc);
+
+   for (k = 0; k < ep->other.desc_count; k++)
+     _vg_unregister_desc_classes(ed,
+        (Edje_Part_Description_Vector *)ep->other.desc[k]);
+}
+
+/* --- End Phase 6.1 helpers ----------------------------------------------- */
+
 static inline void
 _edje_process_colorclass(Edje *ed)
 {
@@ -759,6 +825,19 @@ _edje_process_colorclass(Edje *ed)
              if (desc->color_class)
                efl_observable_observer_add(_edje_color_class_member, desc->color_class, ed->obj);
           }
+
+        /* For VECTOR parts, also register the class names referenced by the
+           description's base tree and override list.  Mirrors RECT/IMAGE/TEXT's
+           per-description registration model. */
+        if (ep->type == EDJE_PART_TYPE_VECTOR && ed->file->vector_dir)
+          {
+             _vg_register_desc_classes(ed,
+                (Edje_Part_Description_Vector *)ep->default_desc);
+
+             for (k = 0; k < ep->other.desc_count; k++)
+               _vg_register_desc_classes(ed,
+                  (Edje_Part_Description_Vector *)ep->other.desc[k]);
+          }
      }
 }
 
diff --git a/src/lib/edje/edje_private.h b/src/lib/edje/edje_private.h
index 4b3c81c3c7..8ffeffe941 100644
--- a/src/lib/edje/edje_private.h
+++ b/src/lib/edje/edje_private.h
@@ -2718,6 +2718,7 @@ Edje_Real_Part   *_edje_real_part_recursive_get(Edje **ed, const char *part);
 // The color_class has to be a pointer to an Eet owned string.
 Edje_Color_Class *_edje_color_class_recursive_find(const Edje *ed, const char *color_class);
 void              _edje_color_class_on_del(Edje *ed, Edje_Part *ep);
+void              _edje_vg_color_class_on_del(Edje *ed, Edje_Part *ep);
 void              _edje_color_class_hash_free(void);
 
 const char       * _edje_find_alias(Eina_Hash *aliased, char *src, int *length);
diff --git a/src/lib/edje/edje_util.c b/src/lib/edje/edje_util.c
index 82f245f356..f1ec6cf44d 100644
--- a/src/lib/edje/edje_util.c
+++ b/src/lib/edje/edje_util.c
@@ -5958,6 +5958,11 @@ _edje_color_class_on_del(Edje *ed, Edje_Part *ep)
    for (i = 0; i < ep->other.desc_count; ++i)
      if (ep->other.desc[i]->color_class)
        efl_observable_observer_del(_edje_color_class_member, ep->other.desc[i]->color_class, ed->obj);
+
+   /* For VECTOR parts, also deregister the class names referenced by the
+      description's base tree and override list (symmetric with the
+      registration performed in _edje_process_colorclass at load time). */
+   _edje_vg_color_class_on_del(ed, ep);
 }
 
 Edje_Text_Class *
diff --git a/src/lib/edje/edje_vg_tree.c b/src/lib/edje/edje_vg_tree.c
index cf83cff73b..553039a433 100644
--- a/src/lib/edje/edje_vg_tree.c
+++ b/src/lib/edje/edje_vg_tree.c
@@ -1780,3 +1780,86 @@ _edje_vg_cache_get_or_build(Edje_Vg_Cache_Slot *slot,
    if (out_working_tree) *out_working_tree  = working;
    return EINA_TRUE;
 }
+
+/* =========================================================================
+ * Phase 6.1 — class-name walk helpers
+ * ========================================================================= */
+
+static void
+_emit_binding_class(const Edje_Vg_Color_Binding *b,
+                    Edje_Vg_Class_Cb cb, void *data)
+{
+   if (b->color_class && *b->color_class)
+     cb(b->color_class, data);
+}
+
+static void
+_node_for_each_class(const Edje_Vg_Node *n,
+                     Edje_Vg_Class_Cb cb, void *data)
+{
+   if (!n) return;
+   _emit_binding_class(&n->color, cb, data);
+   switch (n->type)
+     {
+      case EDJE_VG_NODE_SHAPE:
+        _emit_binding_class(&n->shape.fill,         cb, data);
+        _emit_binding_class(&n->shape.stroke_color, cb, data);
+        break;
+      case EDJE_VG_NODE_CONTAINER:
+        {
+           const Eina_List *l;
+           const Edje_Vg_Node *c;
+           EINA_LIST_FOREACH(n->container.children, l, c)
+             _node_for_each_class(c, cb, data);
+        }
+        break;
+      case EDJE_VG_NODE_GRADIENT_LINEAR:
+      case EDJE_VG_NODE_GRADIENT_RADIAL:
+        {
+           unsigned int i;
+           for (i = 0; i < n->gradient.stops_count; i++)
+             _emit_binding_class(&n->gradient.stops[i].color, cb, data);
+        }
+        break;
+     }
+}
+
+EAPI void
+_edje_vg_tree_for_each_class_name(const Edje_Vg_Tree *t,
+                                  Edje_Vg_Class_Cb cb, void *data)
+{
+   if (!t || !cb) return;
+   _node_for_each_class(t->root, cb, data);
+}
+
+EAPI void
+_edje_vg_overrides_for_each_class_name(const Eina_List *overrides,
+                                       Edje_Vg_Class_Cb cb, void *data)
+{
+   const Eina_List *l;
+   const Edje_Vg_Override *ovr;
+
+   if (!overrides || !cb) return;
+
+   EINA_LIST_FOREACH(overrides, l, ovr)
+     {
+        if (ovr->field_mask & EDJE_VG_OVR_COLOR_CLASS)
+          _emit_binding_class(&ovr->payload.color, cb, data);
+        if (ovr->expected_type == EDJE_VG_NODE_SHAPE)
+          {
+             if (ovr->field_mask & EDJE_VG_OVR_FILL_CLASS)
+               _emit_binding_class(&ovr->payload.shape.fill, cb, data);
+             if (ovr->field_mask & EDJE_VG_OVR_STROKE_CLASS)
+               _emit_binding_class(&ovr->payload.shape.stroke_color, cb, data);
+          }
+        if ((ovr->expected_type == EDJE_VG_NODE_GRADIENT_LINEAR ||
+             ovr->expected_type == EDJE_VG_NODE_GRADIENT_RADIAL) &&
+            (ovr->field_mask & EDJE_VG_OVR_GRAD_STOPS))
+          {
+             unsigned int i;
+             for (i = 0; i < ovr->payload.gradient.stops_count; i++)
+               _emit_binding_class(&ovr->payload.gradient.stops[i].color,
+                                   cb, data);
+          }
+     }
+}
diff --git a/src/lib/edje/edje_vg_tree.h b/src/lib/edje/edje_vg_tree.h
index eb57f2d1b8..1da3651bea 100644
--- a/src/lib/edje/edje_vg_tree.h
+++ b/src/lib/edje/edje_vg_tree.h
@@ -175,6 +175,28 @@ EAPI Eina_Bool _edje_vg_cache_get_or_build(Edje_Vg_Cache_Slot *slot,
    desc_key to NULL.  Safe to call on an already-cleared or zero-init'd slot. */
 EAPI void _edje_vg_cache_slot_clear(Edje_Vg_Cache_Slot *slot);
 
+/* --- Phase 6.1: class-name walk helpers ---------------------------------- */
+
+/* Callback type invoked once per color_class name occurrence found in the
+ * tree.  class_name is a non-NULL, non-empty string owned by the tree
+ * (stringshared); data is the caller-supplied context pointer. */
+typedef void (*Edje_Vg_Class_Cb)(const char *class_name, void *data);
+
+/* Iterate all class names referenced by any Edje_Vg_Color_Binding in
+ * the tree (node-level color, shape fill/stroke, gradient stop colors).
+ * Empty string and NULL are skipped (they signal "no class").  The
+ * callback is invoked once per occurrence — duplicates are NOT filtered
+ * (callers that need a unique set should track via Eina_Hash). */
+EAPI void _edje_vg_tree_for_each_class_name(const Edje_Vg_Tree *t,
+                                            Edje_Vg_Class_Cb cb,
+                                            void *data);
+
+/* Same for an override list.  For each override, inspects field_mask
+ * bits and walks only the touched fields. */
+EAPI void _edje_vg_overrides_for_each_class_name(const Eina_List *overrides,
+                                                 Edje_Vg_Class_Cb cb,
+                                                 void *data);
+
 /* --- 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 5063d71373..d3f3900a5c 100644
--- a/src/tests/edje/data/test_vector_states.edc
+++ b/src/tests/edje/data/test_vector_states.edc
@@ -1,4 +1,8 @@
 collections {
+   color_classes {
+      color_class { name: "vg_accent"; color: 80 140 255 255; }
+      color_class { name: "vg_outline"; color: 30 30 30 255; }
+   }
    vectors {
       vector { name: "tile_a";
          viewbox: 0 0 100 100;
@@ -14,6 +18,15 @@ collections {
             fill.color: 0 200 0 255;
          }
       }
+      /* Task 6.1: fill and stroke bound to color classes */
+      vector { name: "tile_classed"; viewbox: 0 0 100 100;
+         shape { name: "body";
+            path: "M0,0 L100,0 L100,100 L0,100 Z";
+            fill.color_class: "vg_accent";
+            stroke.color_class: "vg_outline";
+            stroke.width: 2;
+         }
+      }
    }
    group { name: "ui/box_a";
       parts { part { name: "vg"; type: VECTOR;
@@ -91,6 +104,14 @@ collections {
          }
       }
    }
+   /* Task 6.1: group that uses tile_classed (fill + stroke color-class bindings). */
+   group { name: "ui/box_classed";
+      parts { part { name: "vg"; type: VECTOR;
+         description { state: "default" 0.0;
+            vector.use: "tile_classed";
+         }
+      } }
+   }
    /* 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
diff --git a/src/tests/edje/edje_test_vector_states.c b/src/tests/edje/edje_test_vector_states.c
index 1ec678299c..03bc3b71af 100644
--- a/src/tests/edje/edje_test_vector_states.c
+++ b/src/tests/edje/edje_test_vector_states.c
@@ -427,8 +427,10 @@ EFL_START_TEST(edje_vg_round_trip_tree_ids)
 
    Edje_Vg_Directory *vd = edf->vector_dir;
 
-   /* Level 1b: fixture has exactly 3 trees. */
-   ck_assert_uint_eq(vd->trees_count, 3);
+   /* Level 1b: fixture has exactly 4 trees (tile_a, tile_b, tile_classed,
+      and the inline tree from ui/box_c).  Updated from 3 in Task 6.1 when
+      tile_classed was added to the vectors{} block. */
+   ck_assert_uint_eq(vd->trees_count, 4);
 
    /* Level 1c: every tree must have a valid root container. */
    for (unsigned int i = 0; i < vd->trees_count; i++)
@@ -1498,6 +1500,81 @@ EFL_START_TEST(edje_vg_cache_hit_on_same_desc)
 }
 EFL_END_TEST
 
+/*
+ * edje_vg_class_member_registered  (Task 6.1)
+ *
+ * Structural test: after loading ui/box_classed the edje object is registered
+ * as an observer of "vg_accent" and "vg_outline".  We confirm this by calling
+ * edje_object_color_class_set for both class names and verifying that:
+ *   (a) the load succeeds,
+ *   (b) the VG part has a non-NULL root node after recalc,
+ *   (c) edje_object_color_class_set + calc_force + render do not crash.
+ *
+ * The actual retint (color resolution) is Task 6.2's job; this test is
+ * intentionally structural-only.
+ */
+EFL_START_TEST(edje_vg_class_member_registered)
+{
+   const char *edj = TESTS_BUILD_DIR "/data/test_vector_states.edj";
+   Ecore_Evas *ee;
+   Evas *evas;
+   Evas_Object *obj;
+   Evas_Object *vg_part;
+   Efl_Canvas_Vg_Node *vg_root;
+
+   ee = ecore_evas_buffer_new(VG_RENDER_W, VG_RENDER_H);
+   fail_if(!ee);
+   ecore_evas_show(ee);
+   ecore_evas_manual_render_set(ee, EINA_TRUE);
+
+   evas = ecore_evas_get(ee);
+   fail_if(!evas);
+
+   obj = edje_object_add(evas);
+   fail_if(!obj);
+
+   /* Load the group whose VG description references tile_classed,
+      which binds fill to "vg_accent" and stroke to "vg_outline". */
+   fail_unless(edje_object_file_set(obj, edj, "ui/box_classed"));
+
+   evas_object_resize(obj, VG_RENDER_W, VG_RENDER_H);
+   evas_object_move(obj, 0, 0);
+   evas_object_show(obj);
+
+   edje_object_calc_force(obj);
+   ecore_evas_manual_render(ee);
+
+   /* (a) VG part must be present and have a materialised root. */
+   vg_part = (Evas_Object *)edje_object_part_object_get(obj, "vg");
+   fail_if(!vg_part);
+   vg_root = efl_canvas_vg_object_root_node_get(vg_part);
+   fail_if(vg_root == NULL);
+
+   /* (b) Change the fill class color — must not crash.
+      Resolution (actual retint) is Task 6.2; here we only exercise the
+      observer dispatch path to confirm registration happened. */
+   edje_object_color_class_set(obj, "vg_accent",
+                                200, 50, 50, 255,
+                                0, 0, 0, 0,
+                                0, 0, 0, 0);
+   edje_object_color_class_set(obj, "vg_outline",
+                                255, 255, 0, 255,
+                                0, 0, 0, 0,
+                                0, 0, 0, 0);
+
+   /* (c) Recalc + render with the new class values — structural check only. */
+   edje_object_calc_force(obj);
+   ecore_evas_manual_render(ee);
+
+   /* Root still present after recalc. */
+   vg_root = efl_canvas_vg_object_root_node_get(vg_part);
+   fail_if(vg_root == NULL);
+
+   evas_object_del(obj);
+   ecore_evas_free(ee);
+}
+EFL_END_TEST
+
 void
 edje_test_vector_states(TCase *tc)
 {
@@ -1527,4 +1604,6 @@ edje_test_vector_states(TCase *tc)
    tcase_add_test(tc, edje_vg_render_box_a_transition);
    /* Phase 5.5 materialized-Efl_VG cache */
    tcase_add_test(tc, edje_vg_cache_hit_on_same_desc);
+   /* Phase 6.1 class-member registration */
+   tcase_add_test(tc, edje_vg_class_member_registered);
 }

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

Reply via email to