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 7c7f9cf505a3dc269abccf36f122d7f83a1a01ec
Author: [email protected] <[email protected]>
AuthorDate: Wed Apr 29 15:09:46 2026 -0600

    edje: resolve color-class bindings at cache rebuild (Phase 6.2)
    
    Wire color-class resolution into Phase 5.5's materialized-Efl_VG cache path.
    When _edje_vg_cache_get_or_build rebuilds, _edje_vg_tree_resolve_colors walks
    all Edje_Vg_Color_Binding nodes (fill, stroke, gradient stops) and multiplies
    raw RGBA × class RGBA / 255, then clears the class_class field so subsequent
    passes (lerp, materializer) see only resolved values per spec §6.5.
    
    Two invalidation paths ensure cache clears when classes change: (1) observer
    path in edje_smart.c when _edje_color_class_member fires, (2) direct setter
    path in edje_util.c for edje_object_color_class_set. Both mark classes_dirty
    on VECTOR parts. Initialize stroke_color default to (255,255,255,255) so
    class-only stroke bindings (no explicit stroke.color) resolve non-zero.
    
    New test edje_vg_runtime_class_retint verifies end-to-end: loads ui/box_classed,
    checks initial stroke = (30,30,30,255), calls edje_object_color_class_set to
    red, recalcs, asserts stroke retints to (255,0,0,255). Skips fill assertion
    pending Task 6.4 (materializer fill-vs-node-color bug).
    
    66/66 tests pass; no valgrind leaks.
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
---
 src/lib/edje/edje_calc.c                 |   4 +-
 src/lib/edje/edje_smart.c                |  17 +++++
 src/lib/edje/edje_util.c                 |   5 ++
 src/lib/edje/edje_vg_tree.c              |  66 +++++++++++++++++++
 src/lib/edje/edje_vg_tree.h              |  15 +++++
 src/tests/edje/edje_test_vector_states.c | 110 +++++++++++++++++++++++++++++--
 6 files changed, 209 insertions(+), 8 deletions(-)

diff --git a/src/lib/edje/edje_calc.c b/src/lib/edje/edje_calc.c
index 07b03be821..5630a5775e 100644
--- a/src/lib/edje/edje_calc.c
+++ b/src/lib/edje/edje_calc.c
@@ -3312,7 +3312,7 @@ _edje_vector_recalc_apply(Edje *ed, Edje_Real_Part *ep, Edje_Calc_Params *p3 EIN
 
         /* Build or fetch the chosen description's cache slot (A side). */
         if (!_edje_vg_cache_get_or_build(&rpv->cache_chosen,
-                                         chosen_desc, base_a,
+                                         ed, chosen_desc, base_a,
                                          chosen_desc->vg.overrides,
                                          rpv->classes_dirty,
                                          &efl_a, &working_a))
@@ -3336,7 +3336,7 @@ _edje_vector_recalc_apply(Edje *ed, Edje_Real_Part *ep, Edje_Calc_Params *p3 EIN
 
                   /* Build or fetch the param2 description's cache slot (B side). */
                   if (_edje_vg_cache_get_or_build(&rpv->cache_param2,
-                                                   next_desc, base_b,
+                                                   ed, next_desc, base_b,
                                                    next_desc->vg.overrides,
                                                    rpv->classes_dirty,
                                                    &efl_b, &working_b))
diff --git a/src/lib/edje/edje_smart.c b/src/lib/edje/edje_smart.c
index d0d794790b..fac96d1e1c 100644
--- a/src/lib/edje/edje_smart.c
+++ b/src/lib/edje/edje_smart.c
@@ -502,6 +502,23 @@ _efl_canvas_layout_efl_observer_update(Eo *obj EINA_UNUSED, Edje *ed, Efl_Object
         ed->all_part_change = EINA_TRUE;
 #endif
      }
+
+   /* Phase 6.2: VECTOR parts use a materialized-Efl_VG cache (Phase 5.5)
+      that needs explicit invalidation on color-class change.  Set
+      classes_dirty on every VECTOR part so the cache rebuild path in
+      _edje_vector_recalc_apply re-resolves class-bound colors. */
+   if (obs == _edje_color_class_member)
+     {
+        unsigned int _i;
+        for (_i = 0; _i < ed->table_parts_size; _i++)
+          {
+             Edje_Real_Part *_rp = ed->table_parts[_i];
+             if (_rp && _rp->part &&
+                 _rp->part->type == EDJE_PART_TYPE_VECTOR &&
+                 _rp->typedata.vector)
+               _rp->typedata.vector->classes_dirty = EINA_TRUE;
+          }
+     }
    else if (obs == _edje_text_class_member)
      {
         if (data == ed)
diff --git a/src/lib/edje/edje_util.c b/src/lib/edje/edje_util.c
index f1ec6cf44d..a0e51c0b1e 100644
--- a/src/lib/edje/edje_util.c
+++ b/src/lib/edje/edje_util.c
@@ -943,6 +943,11 @@ _efl_canvas_layout_efl_gfx_color_class_color_class_set(Eo *obj EINA_UNUSED, Edje
                  (rp->typedata.swallow->swallowed_object))
                efl_gfx_color_class_set(rp->typedata.swallow->swallowed_object,
                                        color_class, layer, r, g, b, a);
+             /* Phase 6.2: invalidate VECTOR part caches so class-bound colors
+                are re-resolved against the updated registry on next recalc. */
+             if (rp->part->type == EDJE_PART_TYPE_VECTOR &&
+                 rp->typedata.vector)
+               rp->typedata.vector->classes_dirty = EINA_TRUE;
           }
 
         _edje_recalc(ed);
diff --git a/src/lib/edje/edje_vg_tree.c b/src/lib/edje/edje_vg_tree.c
index 553039a433..8f4eaf3064 100644
--- a/src/lib/edje/edje_vg_tree.c
+++ b/src/lib/edje/edje_vg_tree.c
@@ -240,6 +240,8 @@ _edje_vg_node_new(Edje_Vg_Node_Type type)
      {
         n->shape.fill.r = n->shape.fill.g =
            n->shape.fill.b = n->shape.fill.a = 255;
+        n->shape.stroke_color.r = n->shape.stroke_color.g =
+           n->shape.stroke_color.b = n->shape.stroke_color.a = 255;
         n->shape.stroke_width = 1.0;
         n->shape.stroke_cap   = EFL_GFX_CAP_BUTT;
         n->shape.stroke_join  = EFL_GFX_JOIN_MITER;
@@ -1729,6 +1731,7 @@ _edje_vg_cache_slot_clear(Edje_Vg_Cache_Slot *slot)
 
 EAPI Eina_Bool
 _edje_vg_cache_get_or_build(Edje_Vg_Cache_Slot *slot,
+                             const Edje *ed,
                              const void *desc_key,
                              const Edje_Vg_Tree *base,
                              const Eina_List *overrides,
@@ -1765,6 +1768,11 @@ _edje_vg_cache_get_or_build(Edje_Vg_Cache_Slot *slot,
           _edje_vg_tree_apply_override(working, ovr);
      }
 
+   /* Resolve class-bound colors before materialization. The cached
+      working_tree carries no remaining class references; subsequent
+      passes (lerp, materialize) see only resolved RGBA per spec §6.5. */
+   _edje_vg_tree_resolve_colors(working, ed);
+
    Efl_VG *efl_vg = _edje_vg_tree_to_efl_vg(working);
    if (!efl_vg)
      {
@@ -1781,6 +1789,64 @@ _edje_vg_cache_get_or_build(Edje_Vg_Cache_Slot *slot,
    return EINA_TRUE;
 }
 
+/* =========================================================================
+ * Phase 6.2 — class color resolution
+ * ========================================================================= */
+
+static void
+_resolve_binding(Edje_Vg_Color_Binding *cb, const Edje *ed)
+{
+   if (!cb || !cb->color_class || !*cb->color_class) return;
+
+   Edje_Color_Class *cc = _edje_color_class_recursive_find(ed, cb->color_class);
+   if (cc)
+     {
+        cb->r = (cb->r * cc->r) / 255;
+        cb->g = (cb->g * cc->g) / 255;
+        cb->b = (cb->b * cc->b) / 255;
+        cb->a = (cb->a * cc->a) / 255;
+     }
+   /* Hit or miss: clear so subsequent passes see resolved RGBA. */
+   eina_stringshare_replace(&cb->color_class, "");
+}
+
+static void
+_resolve_node_recursive(Edje_Vg_Node *n, const Edje *ed)
+{
+   if (!n) return;
+   _resolve_binding(&n->color, ed);
+   switch (n->type)
+     {
+      case EDJE_VG_NODE_SHAPE:
+        _resolve_binding(&n->shape.fill, ed);
+        _resolve_binding(&n->shape.stroke_color, ed);
+        break;
+      case EDJE_VG_NODE_CONTAINER:
+        {
+           Eina_List *l;
+           Edje_Vg_Node *c;
+           EINA_LIST_FOREACH(n->container.children, l, c)
+             _resolve_node_recursive(c, ed);
+        }
+        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++)
+             _resolve_binding(&n->gradient.stops[i].color, ed);
+        }
+        break;
+     }
+}
+
+EAPI void
+_edje_vg_tree_resolve_colors(Edje_Vg_Tree *working, const Edje *ed)
+{
+   if (!working || !ed) return;  /* No-op when ed is NULL — used by tests */
+   _resolve_node_recursive(working->root, ed);
+}
+
 /* =========================================================================
  * Phase 6.1 — class-name walk helpers
  * ========================================================================= */
diff --git a/src/lib/edje/edje_vg_tree.h b/src/lib/edje/edje_vg_tree.h
index 1da3651bea..af3e09a074 100644
--- a/src/lib/edje/edje_vg_tree.h
+++ b/src/lib/edje/edje_vg_tree.h
@@ -164,6 +164,7 @@ EAPI Edje_Vg_Tree *_edje_vg_tree_lerp(const Edje_Vg_Tree *a,
    out_efl_vg and out_working_tree may each be NULL if the caller does not need
    them. */
 EAPI Eina_Bool _edje_vg_cache_get_or_build(Edje_Vg_Cache_Slot *slot,
+                                            const Edje *ed,
                                             const void *desc_key,
                                             const Edje_Vg_Tree *base,
                                             const Eina_List *overrides,
@@ -175,6 +176,20 @@ 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.2: class color resolution ----------------------------------- */
+
+/* Walk the working tree; for every Edje_Vg_Color_Binding whose
+ * color_class is non-NULL/non-empty, look up the class via
+ * _edje_color_class_recursive_find(ed, name). On hit, multiply the
+ * binding's raw RGBA componentwise (resolved.r = base.r * C.r / 255).
+ * Then clear color_class to "" (empty stringshare) so subsequent passes
+ * (lerp / materialize) see only resolved RGBA.
+ *
+ * Empty string is the "no class" sentinel — left untouched. Class
+ * not found: clear the binding's class so subsequent recalcs don't keep
+ * looking it up.  No-op when working or ed is NULL. */
+EAPI void _edje_vg_tree_resolve_colors(Edje_Vg_Tree *working, const Edje *ed);
+
 /* --- Phase 6.1: class-name walk helpers ---------------------------------- */
 
 /* Callback type invoked once per color_class name occurrence found in the
diff --git a/src/tests/edje/edje_test_vector_states.c b/src/tests/edje/edje_test_vector_states.c
index 03bc3b71af..486b9c828c 100644
--- a/src/tests/edje/edje_test_vector_states.c
+++ b/src/tests/edje/edje_test_vector_states.c
@@ -128,8 +128,10 @@ typedef struct
 } Test_Vg_Cache_Slot;
 
 /* The cache helpers take Edje_Vg_Cache_Slot* from the library's perspective,
- * but since both structs have identical layout, we cast freely. */
+ * but since both structs have identical layout, we cast freely.
+ * ed is passed as NULL in unit tests — resolve short-circuits on NULL ed. */
 Eina_Bool _edje_vg_cache_get_or_build(void *slot,
+                                       const void *ed,
                                        const void *desc_key,
                                        const Edje_Vg_Tree *base,
                                        const void *overrides,
@@ -1463,8 +1465,9 @@ EFL_START_TEST(edje_vg_cache_hit_on_same_desc)
    Efl_VG *vg1 = NULL;
    const Edje_Vg_Tree *wt1 = NULL;
 
-   /* First call: cold miss — must build and return valid objects. */
-   fail_if(!_edje_vg_cache_get_or_build(&slot, key_a, base, NULL,
+   /* First call: cold miss — must build and return valid objects.
+      NULL ed: resolve short-circuits (no class names in test tree). */
+   fail_if(!_edje_vg_cache_get_or_build(&slot, NULL, key_a, base, NULL,
                                          EINA_FALSE, &vg1, &wt1));
    fail_if(!vg1);
    fail_if(!wt1);
@@ -1473,7 +1476,7 @@ EFL_START_TEST(edje_vg_cache_hit_on_same_desc)
    const Edje_Vg_Tree *wt2 = NULL;
 
    /* Second call with same key: cache hit — same pointer returned. */
-   fail_if(!_edje_vg_cache_get_or_build(&slot, key_a, base, NULL,
+   fail_if(!_edje_vg_cache_get_or_build(&slot, NULL, key_a, base, NULL,
                                          EINA_FALSE, &vg2, &wt2));
    ck_assert_ptr_eq(vg1, vg2);
    ck_assert_ptr_eq(wt1, wt2);
@@ -1481,14 +1484,14 @@ EFL_START_TEST(edje_vg_cache_hit_on_same_desc)
    Efl_VG *vg3 = NULL;
 
    /* Third call with different key: cache miss — fresh object. */
-   fail_if(!_edje_vg_cache_get_or_build(&slot, key_b, base, NULL,
+   fail_if(!_edje_vg_cache_get_or_build(&slot, NULL, key_b, base, NULL,
                                          EINA_FALSE, &vg3, NULL));
    fail_if(vg3 == vg1);
 
    Efl_VG *vg4 = NULL;
 
    /* Fourth call: same key_b but classes_dirty forces rebuild. */
-   fail_if(!_edje_vg_cache_get_or_build(&slot, key_b, base, NULL,
+   fail_if(!_edje_vg_cache_get_or_build(&slot, NULL, key_b, base, NULL,
                                          EINA_TRUE, &vg4, NULL));
    fail_if(vg4 == vg3);
 
@@ -1575,6 +1578,99 @@ EFL_START_TEST(edje_vg_class_member_registered)
 }
 EFL_END_TEST
 
+/*
+ * edje_vg_runtime_class_retint  (Task 6.2)
+ *
+ * Runtime color-class resolution test using ui/box_classed (tile_classed):
+ *   fill.color_class = "vg_accent"  (EDC default: 80 140 255 255)
+ *   stroke.color_class = "vg_outline" (EDC default: 30 30 30 255)
+ *   Raw binding defaults: 255 255 255 255 (white)
+ *
+ * After initial load+render the materialised stroke should be ~(30, 30, 30, 255)
+ * because resolve does: 255*30/255 = 30 for each channel.
+ *
+ * After edje_object_color_class_set(obj, "vg_outline", 255,0,0,255, ...) + recalc
+ * the stroke should be (255, 0, 0, 255): 255*255/255=255, 255*0/255=0, 255*0/255=0.
+ *
+ * Fill color assertion is skipped per the acknowledged fill-vs-node-color bug
+ * (Task 6.4); stroke has its own independent getter unaffected by that bug.
+ */
+EFL_START_TEST(edje_vg_runtime_class_retint)
+{
+   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;
+   Efl_Canvas_Vg_Node *body_node;
+   int r, g, b, a;
+
+   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);
+
+   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);
+
+   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);
+   fail_if(!efl_isa(vg_root, EFL_CANVAS_VG_CONTAINER_CLASS));
+
+   body_node = efl_canvas_vg_container_child_get(vg_root, "body");
+   fail_if(body_node == NULL);
+
+   /* Initial stroke: raw (255,255,255,255) * vg_outline (30,30,30,255) / 255 = (30,30,30,255) */
+   efl_gfx_shape_stroke_color_get(body_node, &r, &g, &b, &a);
+   ck_assert_int_eq(r, 30);
+   ck_assert_int_eq(g, 30);
+   ck_assert_int_eq(b, 30);
+   ck_assert_int_eq(a, 255);
+
+   /* Change the outline class to red; observer fires -> classes_dirty -> rebuild. */
+   edje_object_color_class_set(obj, "vg_outline",
+                                255, 0, 0, 255,
+                                0, 0, 0, 0,
+                                0, 0, 0, 0);
+   edje_object_calc_force(obj);
+   ecore_evas_manual_render(ee);
+
+   /* Root pointer may have changed after rematerialization. */
+   vg_root = efl_canvas_vg_object_root_node_get(vg_part);
+   fail_if(vg_root == NULL);
+   fail_if(!efl_isa(vg_root, EFL_CANVAS_VG_CONTAINER_CLASS));
+
+   body_node = efl_canvas_vg_container_child_get(vg_root, "body");
+   fail_if(body_node == NULL);
+
+   /* After change: raw (255,255,255,255) * vg_outline (255,0,0,255) / 255 = (255,0,0,255) */
+   efl_gfx_shape_stroke_color_get(body_node, &r, &g, &b, &a);
+   ck_assert_int_eq(r, 255);
+   ck_assert_int_eq(g, 0);
+   ck_assert_int_eq(b, 0);
+   ck_assert_int_eq(a, 255);
+
+   evas_object_del(obj);
+   ecore_evas_free(ee);
+}
+EFL_END_TEST
+
 void
 edje_test_vector_states(TCase *tc)
 {
@@ -1606,4 +1702,6 @@ edje_test_vector_states(TCase *tc)
    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);
+   /* Phase 6.2 runtime color-class resolution */
+   tcase_add_test(tc, edje_vg_runtime_class_retint);
 }

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

Reply via email to