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 b61568fdb94e6f73eaecffc89fba20f9f3c8172d
Author: [email protected] <[email protected]>
AuthorDate: Thu Apr 30 19:31:13 2026 -0600

    edje_edit: add vector-tree and override editing API (Phase 8.2)
    
    This commit introduces 10 new Edje_Edit public API symbols to enable
    visual editors and round-trip tooling (like edje_decc) to manipulate
    named Edje_Vg_Tree entries and per-node overrides end-to-end:
    
      Tree directory (4 symbols):
        edje_edit_vg_trees_list_get()      — enumerate named trees
        edje_edit_vg_tree_exists()         — test tree presence
        edje_edit_vg_tree_add()            — create new empty tree
        edje_edit_vg_tree_del()            — remove tree (with in-use check)
    
      Part-state bindings (4 symbols):
        edje_edit_part_vector_tree_use_get()       — read bound tree name
        edje_edit_part_vector_tree_use_set()       — bind tree to part-state
        edje_edit_part_vector_overrides_list_get() — enumerate overrides
        edje_edit_part_vector_override_{add,del}() — manage overrides
    
      Node properties (2 symbols):
        edje_edit_vector_node_color_class_{get,set}() — bind color classes
    
    API naming: Uses "vg_tree" prefix to avoid collision with the pre-existing
    unrelated edje_edit_vectors_list_get (which returns SVG image-dir entries).
    
    BREAKING FIX: Expose _edje_file_coll_open and update _edje_vg_node_free/
    _edje_vg_tree_free_fields to accept a free_strings flag. This fixes a
    pre-existing stringshare corruption bug that was triggered when any caller
    opened a compiled .edj and let Eet-loaded string dictionaries be freed:
    
      The Eet descriptor decodes strings into Eina_Stringshare handles that
      point into edf->free_strings dictionary. When free_strings==0 (Eet-managed),
      calling eina_stringshare_del on these pointers corrupts the dictionary.
      The new vg_tree_del implementation scans unloaded groups via _edje_file_coll_open
      to check for in-use references; this would have crashed during file cleanup
      when Edf_Free tried to release tree strings that were never stringshare'd.
    
      Fix: Add Eina_Bool free_strings parameter to _edje_vg_node_free and
      _edje_vg_tree_free_fields. Pass edf->free_strings from _edje_file_free to
      prevent stringshare_del calls on Eet-dictionary strings.
    
    Test coverage: 10 unit tests in edje_test_edit_vector.c covering all symbols,
    round-trip save/load, and the memmove+tree_id-- cleanup path in vg_tree_del.
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
---
 src/lib/edje/Edje_Edit.h               | 171 ++++++++++++++
 src/lib/edje/edje_cache.c              |   2 +-
 src/lib/edje/edje_edit.c               | 393 +++++++++++++++++++++++++++++++
 src/lib/edje/edje_load.c               |   3 +-
 src/lib/edje/edje_private.h            |   1 +
 src/lib/edje/edje_vg_tree.c            |  47 ++--
 src/lib/edje/edje_vg_tree.h            |  19 +-
 src/tests/edje/edje_suite.c            |   1 +
 src/tests/edje/edje_suite.h            |   1 +
 src/tests/edje/edje_test_edit_vector.c | 406 +++++++++++++++++++++++++++++++++
 src/tests/edje/meson.build             |   1 +
 11 files changed, 1019 insertions(+), 26 deletions(-)

diff --git a/src/lib/edje/Edje_Edit.h b/src/lib/edje/Edje_Edit.h
index 4de878fe43..90811611aa 100644
--- a/src/lib/edje/Edje_Edit.h
+++ b/src/lib/edje/Edje_Edit.h
@@ -8948,6 +8948,177 @@ EAPI extern Eina_Error EDJE_EDIT_ERROR_GROUP_REFERENCED;
 EAPI extern Eina_Error EDJE_EDIT_ERROR_GROUP_DOES_NOT_EXIST;
 
 
+/******************************************************************************/
+/**************************   VECTOR TREE API   ******************************/
+/******************************************************************************/
+/**
+ * @name Vector Tree API
+ * Functions for manipulating the named Edje_Vg_Tree directory (Phase 8.2).
+ * This API operates on the toplevel @c vectors {} block, which holds named
+ * Edje_Vg_Tree entries.  It is distinct from the SVG-file image-directory
+ * API (edje_edit_vector_del, edje_edit_vectors_list_get, etc.).
+ */ //@{
+
+/**
+ * @brief Returns the list of named vector-tree names in the file.
+ *
+ * Use edje_edit_string_list_free() to release the returned list.
+ *
+ * @param obj Object being edited.
+ *
+ * @return A list of Eina_Stringshare names, one per named Edje_Vg_Tree.
+ * @since 1.27
+ */
+EAPI Eina_List *edje_edit_vg_trees_list_get(Evas_Object *obj);
+
+/**
+ * @brief Tests whether a named vector tree exists in the file.
+ *
+ * @param obj Object being edited.
+ * @param name The tree name to look up.
+ *
+ * @return @c EINA_TRUE if a tree with that name is present, @c EINA_FALSE otherwise.
+ * @since 1.27
+ */
+EAPI Eina_Bool edje_edit_vg_tree_exists(Evas_Object *obj, const char *name);
+
+/**
+ * @brief Adds a new empty named vector tree to the file.
+ *
+ * Returns @c EINA_FALSE if a tree with that name already exists.
+ *
+ * @param obj Object being edited.
+ * @param name The name for the new tree.
+ *
+ * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise.
+ * @since 1.27
+ */
+EAPI Eina_Bool edje_edit_vg_tree_add(Evas_Object *obj, const char *name);
+
+/**
+ * @brief Removes a named vector tree from the file.
+ *
+ * Refuses and returns @c EINA_FALSE if any description's tree_id references
+ * this tree (which would leave the file inconsistent).
+ *
+ * @param obj Object being edited.
+ * @param name The name of the tree to remove.
+ *
+ * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise.
+ * @since 1.27
+ */
+EAPI Eina_Bool edje_edit_vg_tree_del(Evas_Object *obj, const char *name);
+
+/**
+ * @brief Gets the named vector tree bound to the given part-state.
+ *
+ * The returned string is a newly added Eina_Stringshare; release with
+ * edje_edit_string_free().  Returns NULL if no tree is bound (tree_id == -1)
+ * or if the part is not of type VECTOR.
+ *
+ * @param obj Object being edited.
+ * @param part Part name.
+ * @param state State name (without value).
+ * @param value State value.
+ *
+ * @return The tree name or NULL.
+ * @since 1.27
+ */
+EAPI const char *edje_edit_part_vector_tree_use_get(Evas_Object *obj, const char *part, const char *state, double value);
+
+/**
+ * @brief Binds a named vector tree to the given part-state.
+ *
+ * Passing NULL for @p tree_name clears the binding (sets tree_id to -1).
+ *
+ * @param obj Object being edited.
+ * @param part Part name.
+ * @param state State name (without value).
+ * @param value State value.
+ * @param tree_name The tree to bind, or NULL to clear.
+ *
+ * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise.
+ * @since 1.27
+ */
+EAPI Eina_Bool edje_edit_part_vector_tree_use_set(Evas_Object *obj, const char *part, const char *state, double value, const char *tree_name);
+
+/**
+ * @brief Returns the list of override target names for a part-state.
+ *
+ * Use edje_edit_string_list_free() to release the returned list.
+ *
+ * @param obj Object being edited.
+ * @param part Part name.
+ * @param state State name (without value).
+ * @param value State value.
+ *
+ * @return A list of Eina_Stringshare target names.
+ * @since 1.27
+ */
+EAPI Eina_List *edje_edit_part_vector_overrides_list_get(Evas_Object *obj, const char *part, const char *state, double value);
+
+/**
+ * @brief Adds an override entry for the given target node name.
+ *
+ * Returns @c EINA_FALSE if an override with that target already exists or on error.
+ *
+ * @param obj Object being edited.
+ * @param part Part name.
+ * @param state State name (without value).
+ * @param value State value.
+ * @param target Target node name.
+ *
+ * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise.
+ * @since 1.27
+ */
+EAPI Eina_Bool edje_edit_part_vector_override_add(Evas_Object *obj, const char *part, const char *state, double value, const char *target);
+
+/**
+ * @brief Removes the override entry for the given target node name.
+ *
+ * @param obj Object being edited.
+ * @param part Part name.
+ * @param state State name (without value).
+ * @param value State value.
+ * @param target Target node name.
+ *
+ * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise.
+ * @since 1.27
+ */
+EAPI Eina_Bool edje_edit_part_vector_override_del(Evas_Object *obj, const char *part, const char *state, double value, const char *target);
+
+/**
+ * @brief Gets the color-class binding on a node in a named toplevel tree.
+ *
+ * The returned string is a newly added Eina_Stringshare; release with
+ * edje_edit_string_free().
+ *
+ * @param obj Object being edited.
+ * @param tree The named tree to look in.
+ * @param node The node name within that tree.
+ *
+ * @return The color-class name or NULL if none is set.
+ * @since 1.27
+ */
+EAPI const char *edje_edit_vector_node_color_class_get(Evas_Object *obj, const char *tree, const char *node);
+
+/**
+ * @brief Sets the color-class binding on a node in a named toplevel tree.
+ *
+ * Passing NULL for @p class_name clears the binding.
+ *
+ * @param obj Object being edited.
+ * @param tree The named tree.
+ * @param node The node name within that tree.
+ * @param class_name The color class to bind, or NULL to clear.
+ *
+ * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise.
+ * @since 1.27
+ */
+EAPI Eina_Bool edje_edit_vector_node_color_class_set(Evas_Object *obj, const char *tree, const char *node, const char *class_name);
+
+//@}
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/lib/edje/edje_cache.c b/src/lib/edje/edje_cache.c
index 9d2cbbd210..8604d19dda 100644
--- a/src/lib/edje/edje_cache.c
+++ b/src/lib/edje/edje_cache.c
@@ -245,7 +245,7 @@ _edje_part_collection_fix(Edje_Part_Collection *edc)
      eina_array_flush(&hist);
 }
 
-static Edje_Part_Collection *
+Edje_Part_Collection *
 _edje_file_coll_open(Edje_File *edf, const char *coll)
 {
    Edje_Part_Collection *edc = NULL;
diff --git a/src/lib/edje/edje_edit.c b/src/lib/edje/edje_edit.c
index d54671d657..48f0ccb846 100644
--- a/src/lib/edje/edje_edit.c
+++ b/src/lib/edje/edje_edit.c
@@ -17251,6 +17251,399 @@ edje_edit_print_internal_status(Evas_Object *obj)
  */
 }
 
+/* -------------------------------------------------------------------------
+ * Vector-tree directory API (Phase 8.2)
+ *
+ * These symbols operate on Edje_File.vector_dir — the named Edje_Vg_Tree
+ * directory — as opposed to the image_dir vectors (SVG files stored as
+ * image resources).  Naming uses the vg_tree_ infix to avoid collision with
+ * the existing edje_edit_vectors_list_get (image_dir) symbol.
+ * ------------------------------------------------------------------------- */
+
+/* Helper: find a named Edje_Vg_Tree in the file's vector_dir.
+ * Returns the tree array index or -1 if not found. */
+static int
+_edje_edit_vg_tree_id_find(const Edje *ed, const char *name)
+{
+   unsigned int i;
+
+   if (!ed->file || !ed->file->vector_dir || !name) return -1;
+   for (i = 0; i < ed->file->vector_dir->trees_count; ++i)
+     if (ed->file->vector_dir->trees[i].name &&
+         !strcmp(name, ed->file->vector_dir->trees[i].name))
+       return (int)i;
+   return -1;
+}
+
+EAPI Eina_List *
+edje_edit_vg_trees_list_get(Evas_Object *obj)
+{
+   Eina_List *lst = NULL;
+   unsigned int i;
+
+   GET_ED_OR_RETURN(NULL);
+   if (!ed->file || !ed->file->vector_dir) return NULL;
+
+   for (i = 0; i < ed->file->vector_dir->trees_count; ++i)
+     {
+        if (ed->file->vector_dir->trees[i].name)
+          lst = eina_list_append(lst,
+                   eina_stringshare_add(ed->file->vector_dir->trees[i].name));
+     }
+   return lst;
+}
+
+EAPI Eina_Bool
+edje_edit_vg_tree_exists(Evas_Object *obj, const char *name)
+{
+   GET_EED_OR_RETURN(EINA_FALSE);
+   if (!name) return EINA_FALSE;
+   return (_edje_edit_vg_tree_id_find(eed->base, name) >= 0)
+          ? EINA_TRUE : EINA_FALSE;
+}
+
+EAPI Eina_Bool
+edje_edit_vg_tree_add(Evas_Object *obj, const char *name)
+{
+   Edje_Vg_Directory *vd;
+   Edje_Vg_Tree *tmp;
+   unsigned int count;
+
+   GET_EED_OR_RETURN(EINA_FALSE);
+   GET_ED_OR_RETURN(EINA_FALSE);
+   if (!name) return EINA_FALSE;
+   if (!ed->file) return EINA_FALSE;
+
+   if (_edje_edit_vg_tree_id_find(ed, name) >= 0) return EINA_FALSE;
+
+   if (!ed->file->vector_dir)
+     {
+        ed->file->vector_dir = _alloc(sizeof(Edje_Vg_Directory));
+        if (!ed->file->vector_dir) return EINA_FALSE;
+     }
+
+   vd = ed->file->vector_dir;
+   count = vd->trees_count + 1;
+   tmp = realloc(vd->trees, sizeof(Edje_Vg_Tree) * count);
+   if (!tmp) return EINA_FALSE;
+
+   vd->trees = tmp;
+   memset(&vd->trees[vd->trees_count], 0, sizeof(Edje_Vg_Tree));
+   vd->trees[vd->trees_count].name = eina_stringshare_add(name);
+   vd->trees_count = count;
+
+   _edje_edit_flag_script_dirty(eed, EINA_TRUE);
+   return EINA_TRUE;
+}
+
+EAPI Eina_Bool
+edje_edit_vg_tree_del(Evas_Object *obj, const char *name)
+{
+   Edje_Vg_Directory *vd;
+   int tree_idx;
+   unsigned int pi, di;
+
+   GET_EED_OR_RETURN(EINA_FALSE);
+   GET_ED_OR_RETURN(EINA_FALSE);
+   if (!name) return EINA_FALSE;
+   if (!ed->file || !ed->file->vector_dir) return EINA_FALSE;
+
+   tree_idx = _edje_edit_vg_tree_id_find(ed, name);
+   if (tree_idx < 0) return EINA_FALSE;
+
+   /* Refuse if any description in any collection references this tree_id.
+    * We must cover BOTH loaded (ce->ref != NULL) and unloaded collections —
+    * unloaded ones are paged in transiently via _edje_file_coll_open so that
+    * a tree referenced only by an unloaded group cannot be silently deleted.
+    * _edje_file_coll_open / _edje_cache_coll_unref use the cache's proper
+    * allocators and avoid the stringshare mismatch that raw eet_data_read
+    * would cause when _edje_collection_free tries to eina_stringshare_del
+    * strings allocated as plain strdup by EET_T_STRING fields. */
+   if (ed->file->collection)
+     {
+        Eina_Iterator *it = eina_hash_iterator_data_new(ed->file->collection);
+        Edje_Part_Collection_Directory_Entry *ce;
+
+        EINA_ITERATOR_FOREACH(it, ce)
+          {
+             Edje_Part_Collection *col;
+             Eina_Bool transient = EINA_FALSE;
+
+             if (ce->ref)
+               {
+                  col = ce->ref;
+               }
+             else
+               {
+                  /* Page in from the cache / disk. Increments reference count
+                   * so we must call _edje_cache_coll_unref when done. */
+                  col = _edje_file_coll_open(ed->file, ce->entry);
+                  transient = EINA_TRUE;
+               }
+
+             if (!col) continue;
+
+             for (pi = 0; pi < col->parts_count; ++pi)
+               {
+                  Edje_Part *ep = col->parts[pi];
+                  if (ep->type != EDJE_PART_TYPE_VECTOR) continue;
+                  for (di = 0; di < ep->other.desc_count + 1; ++di)
+                    {
+                       Edje_Part_Description_Vector *vdesc;
+                       Edje_Part_Description_Common *base;
+
+                       base = (di == 0) ? ep->default_desc
+                                        : ep->other.desc[di - 1];
+                       if (!base) continue;
+                       vdesc = (Edje_Part_Description_Vector *)base;
+                       if (vdesc->vg.tree_id == tree_idx)
+                         {
+                            if (transient)
+                              _edje_cache_coll_unref(ed->file, col);
+                            eina_iterator_free(it);
+                            return EINA_FALSE;
+                         }
+                    }
+               }
+
+             if (transient) _edje_cache_coll_unref(ed->file, col);
+          }
+
+        eina_iterator_free(it);
+     }
+
+   vd = ed->file->vector_dir;
+   /* Free the tree's own fields (name + root node subtree). _edje_vg_tree_free_fields
+    * handles t->name via eina_stringshare_del. In edje_edit, trees are always
+    * built with proper stringshares, so pass EINA_TRUE. */
+   _edje_vg_tree_free_fields(&vd->trees[tree_idx], EINA_TRUE);
+
+   if ((unsigned int)tree_idx < vd->trees_count - 1)
+     {
+        /* Shift the tail down one slot. */
+        memmove(&vd->trees[tree_idx], &vd->trees[tree_idx + 1],
+                sizeof(Edje_Vg_Tree) * (vd->trees_count - (unsigned int)tree_idx - 1));
+
+        /* Fix up tree_id references that pointed past the removed slot. */
+        if (ed->file->collection)
+          {
+             Eina_Iterator *it2 =
+                eina_hash_iterator_data_new(ed->file->collection);
+             Edje_Part_Collection_Directory_Entry *ce2;
+
+             EINA_ITERATOR_FOREACH(it2, ce2)
+               {
+                  Edje_Part_Collection *col = ce2->ref;
+                  if (!col) continue;
+                  for (pi = 0; pi < col->parts_count; ++pi)
+                    {
+                       Edje_Part *ep = col->parts[pi];
+                       if (ep->type != EDJE_PART_TYPE_VECTOR) continue;
+                       for (di = 0; di < ep->other.desc_count + 1; ++di)
+                         {
+                            Edje_Part_Description_Vector *vdesc;
+                            Edje_Part_Description_Common *base;
+
+                            base = (di == 0) ? ep->default_desc
+                                             : ep->other.desc[di - 1];
+                            if (!base) continue;
+                            vdesc = (Edje_Part_Description_Vector *)base;
+                            if (vdesc->vg.tree_id > tree_idx)
+                              vdesc->vg.tree_id--;
+                         }
+                    }
+               }
+             eina_iterator_free(it2);
+          }
+     }
+
+   vd->trees_count--;
+   _edje_edit_flag_script_dirty(eed, EINA_TRUE);
+   return EINA_TRUE;
+}
+
+/* -----------------------------------------------------------------------
+ * Per-state tree binding
+ * ----------------------------------------------------------------------- */
+
+EAPI const char *
+edje_edit_part_vector_tree_use_get(Evas_Object *obj, const char *part,
+                                   const char *state, double value)
+{
+   Edje_Part_Description_Vector *vdesc;
+
+   GET_PD_OR_RETURN(NULL);
+
+   if (rp->part->type != EDJE_PART_TYPE_VECTOR) return NULL;
+   vdesc = (Edje_Part_Description_Vector *)pd;
+   if (vdesc->vg.tree_id < 0) return NULL;
+   if (!ed->file || !ed->file->vector_dir) return NULL;
+   if ((unsigned int)vdesc->vg.tree_id >= ed->file->vector_dir->trees_count)
+     return NULL;
+   return eina_stringshare_add(
+      ed->file->vector_dir->trees[vdesc->vg.tree_id].name);
+}
+
+EAPI Eina_Bool
+edje_edit_part_vector_tree_use_set(Evas_Object *obj, const char *part,
+                                   const char *state, double value,
+                                   const char *tree_name)
+{
+   Edje_Part_Description_Vector *vdesc;
+   int tid;
+
+   GET_PD_OR_RETURN(EINA_FALSE);
+
+   if (rp->part->type != EDJE_PART_TYPE_VECTOR) return EINA_FALSE;
+   vdesc = (Edje_Part_Description_Vector *)pd;
+
+   if (!tree_name)
+     {
+        vdesc->vg.tree_id = -1;
+        _edje_edit_flag_script_dirty(eed, EINA_TRUE);
+        return EINA_TRUE;
+     }
+
+   tid = _edje_edit_vg_tree_id_find(ed, tree_name);
+   if (tid < 0) return EINA_FALSE;
+   vdesc->vg.tree_id = tid;
+   _edje_edit_flag_script_dirty(eed, EINA_TRUE);
+   return EINA_TRUE;
+}
+
+/* -----------------------------------------------------------------------
+ * Per-state override list
+ * ----------------------------------------------------------------------- */
+
+EAPI Eina_List *
+edje_edit_part_vector_overrides_list_get(Evas_Object *obj, const char *part,
+                                         const char *state, double value)
+{
+   Edje_Part_Description_Vector *vdesc;
+   Eina_List *out = NULL, *l;
+   Edje_Vg_Override *ovr;
+
+   GET_PD_OR_RETURN(NULL);
+
+   if (rp->part->type != EDJE_PART_TYPE_VECTOR) return NULL;
+   vdesc = (Edje_Part_Description_Vector *)pd;
+
+   EINA_LIST_FOREACH(vdesc->vg.overrides, l, ovr)
+     {
+        if (ovr->target_name)
+          out = eina_list_append(out, eina_stringshare_add(ovr->target_name));
+     }
+   return out;
+}
+
+EAPI Eina_Bool
+edje_edit_part_vector_override_add(Evas_Object *obj, const char *part,
+                                   const char *state, double value,
+                                   const char *target)
+{
+   Edje_Part_Description_Vector *vdesc;
+   Eina_List *l;
+   Edje_Vg_Override *ovr;
+   Edje_Vg_Override *novr;
+
+   GET_PD_OR_RETURN(EINA_FALSE);
+
+   if (!target) return EINA_FALSE;
+   if (rp->part->type != EDJE_PART_TYPE_VECTOR) return EINA_FALSE;
+   vdesc = (Edje_Part_Description_Vector *)pd;
+
+   EINA_LIST_FOREACH(vdesc->vg.overrides, l, ovr)
+     if (ovr->target_name && !strcmp(ovr->target_name, target))
+       return EINA_FALSE;
+
+   novr = _alloc(sizeof(Edje_Vg_Override));
+   if (!novr) return EINA_FALSE;
+   novr->target_name = eina_stringshare_add(target);
+   novr->field_mask  = 0;
+   vdesc->vg.overrides = eina_list_append(vdesc->vg.overrides, novr);
+   _edje_edit_flag_script_dirty(eed, EINA_TRUE);
+   return EINA_TRUE;
+}
+
+EAPI Eina_Bool
+edje_edit_part_vector_override_del(Evas_Object *obj, const char *part,
+                                   const char *state, double value,
+                                   const char *target)
+{
+   Edje_Part_Description_Vector *vdesc;
+   Eina_List *l;
+   Edje_Vg_Override *ovr;
+
+   GET_PD_OR_RETURN(EINA_FALSE);
+
+   if (!target) return EINA_FALSE;
+   if (rp->part->type != EDJE_PART_TYPE_VECTOR) return EINA_FALSE;
+   vdesc = (Edje_Part_Description_Vector *)pd;
+
+   EINA_LIST_FOREACH(vdesc->vg.overrides, l, ovr)
+     {
+        if (ovr->target_name && !strcmp(ovr->target_name, target))
+          {
+             vdesc->vg.overrides = eina_list_remove(vdesc->vg.overrides, ovr);
+             eina_stringshare_del(ovr->target_name);
+             free(ovr);
+             _edje_edit_flag_script_dirty(eed, EINA_TRUE);
+             return EINA_TRUE;
+          }
+     }
+   return EINA_FALSE;
+}
+
+/* -----------------------------------------------------------------------
+ * Per-node color-class binding (on toplevel-directory trees)
+ * ----------------------------------------------------------------------- */
+
+EAPI const char *
+edje_edit_vector_node_color_class_get(Evas_Object *obj, const char *tree,
+                                      const char *node)
+{
+   Edje_Vg_Tree *t;
+   Edje_Vg_Node *n;
+   int tid;
+
+   GET_EED_OR_RETURN(NULL);
+   if (!tree || !node) return NULL;
+
+   tid = _edje_edit_vg_tree_id_find(eed->base, tree);
+   if (tid < 0) return NULL;
+   t = &eed->base->file->vector_dir->trees[tid];
+
+   n = _edje_vg_tree_node_by_name(t, node);
+   if (!n) return NULL;
+
+   if (!n->color.color_class) return NULL;
+   return eina_stringshare_add(n->color.color_class);
+}
+
+EAPI Eina_Bool
+edje_edit_vector_node_color_class_set(Evas_Object *obj, const char *tree,
+                                      const char *node, const char *class_name)
+{
+   Edje_Vg_Tree *t;
+   Edje_Vg_Node *n;
+   int tid;
+
+   GET_EED_OR_RETURN(EINA_FALSE);
+   if (!tree || !node) return EINA_FALSE;
+
+   tid = _edje_edit_vg_tree_id_find(eed->base, tree);
+   if (tid < 0) return EINA_FALSE;
+   t = &eed->base->file->vector_dir->trees[tid];
+
+   n = _edje_vg_tree_node_by_name(t, node);
+   if (!n) return EINA_FALSE;
+
+   eina_stringshare_del(n->color.color_class);
+   n->color.color_class = class_name ? eina_stringshare_add(class_name) : NULL;
+   _edje_edit_flag_script_dirty(eed, EINA_TRUE);
+   return EINA_TRUE;
+}
+
 /* Internal EO APIs and hidden overrides */
 
 #define EDJE_EDIT_EXTRA_OPS \
diff --git a/src/lib/edje/edje_load.c b/src/lib/edje/edje_load.c
index cfb16fb7ec..043413b562 100644
--- a/src/lib/edje/edje_load.c
+++ b/src/lib/edje/edje_load.c
@@ -2294,7 +2294,8 @@ _edje_file_free(Edje_File *edf)
          * Free each tree's interior heap allocations (root node tree),
          * then free the array itself. */
         for (i = 0; i < edf->vector_dir->trees_count; i++)
-          _edje_vg_tree_free_fields(&edf->vector_dir->trees[i]);
+          _edje_vg_tree_free_fields(&edf->vector_dir->trees[i],
+                                     edf->free_strings);
         free(edf->vector_dir->trees);
 
         /* class_names is a flat array of stringshare strings (Eet decodes
diff --git a/src/lib/edje/edje_private.h b/src/lib/edje/edje_private.h
index f678bbd498..ee7567956c 100644
--- a/src/lib/edje/edje_private.h
+++ b/src/lib/edje/edje_private.h
@@ -2854,6 +2854,7 @@ Edje_File *_edje_file_cache_find(const Eina_File *file);
 void _edje_cache_coll_clean(Edje_File *edf);
 void _edje_cache_coll_flush(Edje_File *edf);
 void _edje_cache_coll_unref(Edje_File *edf, Edje_Part_Collection *edc);
+Edje_Part_Collection *_edje_file_coll_open(Edje_File *edf, const char *coll);
 EAPI void edje_cache_emp_alloc(Edje_Part_Collection_Directory_Entry *ce);
 EAPI void edje_cache_emp_free(Edje_Part_Collection_Directory_Entry *ce);
 EAPI void _edje_cache_file_unref(Edje_File *edf);
diff --git a/src/lib/edje/edje_vg_tree.c b/src/lib/edje/edje_vg_tree.c
index 26f7a09848..15ed5e9d0c 100644
--- a/src/lib/edje/edje_vg_tree.c
+++ b/src/lib/edje/edje_vg_tree.c
@@ -146,26 +146,27 @@ _color_binding_free_fields(Edje_Vg_Color_Binding *cb)
 }
 
 EAPI void
-_edje_vg_node_free(Edje_Vg_Node *n)
+_edje_vg_node_free(Edje_Vg_Node *n, Eina_Bool free_strings)
 {
    if (!n) return;
 
    /* common fields */
-   if (n->name) { eina_stringshare_del(n->name); n->name = NULL; }
-   _color_binding_free_fields(&n->color);
+   if (free_strings && n->name)
+     { eina_stringshare_del(n->name); n->name = NULL; }
+   if (free_strings && n->color.color_class)
+     { eina_stringshare_del(n->color.color_class); n->color.color_class = NULL; }
 
    switch (n->type)
      {
       case EDJE_VG_NODE_SHAPE:
-         if (n->shape.path)
+         if (free_strings && n->shape.path)
            { eina_stringshare_del(n->shape.path); n->shape.path = NULL; }
-         _color_binding_free_fields(&n->shape.fill);
-         _color_binding_free_fields(&n->shape.stroke_color);
-         if (n->shape.gradient_ref)
-           {
-              eina_stringshare_del(n->shape.gradient_ref);
-              n->shape.gradient_ref = NULL;
-           }
+         if (free_strings && n->shape.fill.color_class)
+           { eina_stringshare_del(n->shape.fill.color_class); n->shape.fill.color_class = NULL; }
+         if (free_strings && n->shape.stroke_color.color_class)
+           { eina_stringshare_del(n->shape.stroke_color.color_class); n->shape.stroke_color.color_class = NULL; }
+         if (free_strings && n->shape.gradient_ref)
+           { eina_stringshare_del(n->shape.gradient_ref); n->shape.gradient_ref = NULL; }
          free(n->shape.stroke_dash);
          n->shape.stroke_dash = NULL;
          break;
@@ -174,7 +175,7 @@ _edje_vg_node_free(Edje_Vg_Node *n)
         {
            Edje_Vg_Node *child;
            EINA_LIST_FREE(n->container.children, child)
-             _edje_vg_node_free(child);
+             _edje_vg_node_free(child, free_strings);
         }
         break;
 
@@ -182,9 +183,16 @@ _edje_vg_node_free(Edje_Vg_Node *n)
       case EDJE_VG_NODE_GRADIENT_RADIAL:
          if (n->gradient.stops)
            {
-              unsigned int i;
-              for (i = 0; i < n->gradient.stops_count; i++)
-                 _color_binding_free_fields(&n->gradient.stops[i].color);
+              if (free_strings)
+                {
+                   unsigned int i;
+                   for (i = 0; i < n->gradient.stops_count; i++)
+                     if (n->gradient.stops[i].color.color_class)
+                       {
+                          eina_stringshare_del(n->gradient.stops[i].color.color_class);
+                          n->gradient.stops[i].color.color_class = NULL;
+                       }
+                }
               free(n->gradient.stops);
               n->gradient.stops = NULL;
            }
@@ -198,11 +206,12 @@ _edje_vg_node_free(Edje_Vg_Node *n)
 }
 
 EAPI void
-_edje_vg_tree_free_fields(Edje_Vg_Tree *t)
+_edje_vg_tree_free_fields(Edje_Vg_Tree *t, Eina_Bool free_strings)
 {
    if (!t) return;
-   if (t->name) { eina_stringshare_del(t->name); t->name = NULL; }
-   _edje_vg_node_free(t->root);
+   if (free_strings && t->name)
+     { eina_stringshare_del(t->name); t->name = NULL; }
+   _edje_vg_node_free(t->root, free_strings);
    t->root = NULL;
 }
 
@@ -210,7 +219,7 @@ EAPI void
 _edje_vg_tree_free(Edje_Vg_Tree *t)
 {
    if (!t) return;
-   _edje_vg_tree_free_fields(t);
+   _edje_vg_tree_free_fields(t, EINA_TRUE);
    free(t);
 }
 
diff --git a/src/lib/edje/edje_vg_tree.h b/src/lib/edje/edje_vg_tree.h
index 819fbddc4b..7e532284e6 100644
--- a/src/lib/edje/edje_vg_tree.h
+++ b/src/lib/edje/edje_vg_tree.h
@@ -22,10 +22,17 @@ EAPI Eina_Bool            _edje_vg_tree_equal(const Edje_Vg_Tree *a,
 
 /* Free interior heap allocations (root node tree) without freeing t itself.
  * Use when t is embedded in a flat array rather than individually heap-alloc'd.
- * Safe to call with NULL. */
-EAPI void                 _edje_vg_tree_free_fields(Edje_Vg_Tree *t);
+ * Safe to call with NULL.
+ * free_strings: EINA_TRUE  — call eina_stringshare_del on all string fields
+ *                             (use when strings were built with eina_stringshare_add
+ *                              by the parser or edje_edit helpers).
+ *               EINA_FALSE — skip string frees; strings point directly into a
+ *                             memory-mapped Eet dictionary (edf->free_strings==0). */
+EAPI void                 _edje_vg_tree_free_fields(Edje_Vg_Tree *t,
+                                                     Eina_Bool free_strings);
 
-/* Recursive free.  Safe to call with NULL. */
+/* Recursive free.  Always frees strings (caller owns them as stringshares).
+ * Safe to call with NULL. */
 EAPI void                 _edje_vg_tree_free(Edje_Vg_Tree *t);
 
 /* --- Construction helpers (Phase 2+) ------------------------------------ */
@@ -39,8 +46,10 @@ EAPI Edje_Vg_Tree *_edje_vg_tree_new(void);
  * 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);
+/* Recursive free.  free_strings follows the same convention as
+ * _edje_vg_tree_free_fields — pass EINA_TRUE when strings are stringshares,
+ * EINA_FALSE when they are Eet dictionary pointers. */
+EAPI void          _edje_vg_node_free(Edje_Vg_Node *n, Eina_Bool free_strings);
 
 /* Append child to parent (parent must be CONTAINER). No-op on bad args. */
 EAPI void          _edje_vg_container_append(Edje_Vg_Node *parent,
diff --git a/src/tests/edje/edje_suite.c b/src/tests/edje/edje_suite.c
index 69120c1a12..3928f2a6fb 100644
--- a/src/tests/edje/edje_suite.c
+++ b/src/tests/edje/edje_suite.c
@@ -17,6 +17,7 @@ static const Efl_Test_Case etc[] = {
   { "Text", edje_test_text },
   { "Edje Text", edje_test_text },
   { "Vector States", edje_test_vector_states },
+  { "Edit Vector", edje_test_edit_vector },
   { NULL, NULL }
 };
 
diff --git a/src/tests/edje/edje_suite.h b/src/tests/edje/edje_suite.h
index cbd2ebe7f2..c97fb2956d 100644
--- a/src/tests/edje/edje_suite.h
+++ b/src/tests/edje/edje_suite.h
@@ -14,6 +14,7 @@ void edje_test_signal(TCase *tc);
 void edje_test_swallow(TCase *tc);
 void edje_test_text(TCase *tc);
 void edje_test_vector_states(TCase *tc);
+void edje_test_edit_vector(TCase *tc);
 
 
 #endif /* _EDJE_SUITE_H */
diff --git a/src/tests/edje/edje_test_edit_vector.c b/src/tests/edje/edje_test_edit_vector.c
new file mode 100644
index 0000000000..18868040ed
--- /dev/null
+++ b/src/tests/edje/edje_test_edit_vector.c
@@ -0,0 +1,406 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <Eina.h>
+#include <Ecore.h>
+#include <Ecore_Evas.h>
+#include "edje_suite.h"
+#define EDJE_EDIT_IS_UNSTABLE_AND_I_KNOW_ABOUT_IT
+#include <Edje_Edit.h>
+
+/* Helper: open the pre-compiled test fixture via edje_edit_object_add. */
+static Evas_Object *
+_edit_obj_new(Ecore_Evas *ee, const char *group)
+{
+   Evas_Object *obj = edje_edit_object_add(ecore_evas_get(ee));
+   if (!obj) return NULL;
+   if (!edje_object_file_set(obj,
+                             TESTS_BUILD_DIR "/data/test_vector_states.edj",
+                             group))
+     {
+        evas_object_del(obj);
+        return NULL;
+     }
+   return obj;
+}
+
+/* -----------------------------------------------------------------------
+ * edje_edit_vg_trees_list_get
+ * ----------------------------------------------------------------------- */
+START_TEST(test_vg_trees_list_get)
+{
+   Ecore_Evas *ee = ecore_evas_buffer_new(1, 1);
+   fail_if(!ee);
+   Evas_Object *obj = _edit_obj_new(ee, "ui/box_a");
+   fail_if(!obj);
+
+   Eina_List *lst = edje_edit_vg_trees_list_get(obj);
+   /* The fixture defines three named trees: tile_a, tile_b, tile_classed. */
+   ck_assert_int_ge((int)eina_list_count(lst), 3);
+   edje_edit_string_list_free(lst);
+
+   evas_object_del(obj);
+   ecore_evas_free(ee);
+}
+END_TEST
+
+/* -----------------------------------------------------------------------
+ * edje_edit_vg_tree_exists
+ * ----------------------------------------------------------------------- */
+START_TEST(test_vg_tree_exists)
+{
+   Ecore_Evas *ee = ecore_evas_buffer_new(1, 1);
+   fail_if(!ee);
+   Evas_Object *obj = _edit_obj_new(ee, "ui/box_a");
+   fail_if(!obj);
+
+   ck_assert(edje_edit_vg_tree_exists(obj, "tile_a") == EINA_TRUE);
+   ck_assert(edje_edit_vg_tree_exists(obj, "tile_b") == EINA_TRUE);
+   ck_assert(edje_edit_vg_tree_exists(obj, "no_such_tree") == EINA_FALSE);
+   ck_assert(edje_edit_vg_tree_exists(obj, NULL) == EINA_FALSE);
+
+   evas_object_del(obj);
+   ecore_evas_free(ee);
+}
+END_TEST
+
+/* -----------------------------------------------------------------------
+ * edje_edit_vg_tree_add — duplicate name returns EINA_FALSE
+ * ----------------------------------------------------------------------- */
+START_TEST(test_vg_tree_add)
+{
+   Ecore_Evas *ee = ecore_evas_buffer_new(1, 1);
+   fail_if(!ee);
+   Evas_Object *obj = _edit_obj_new(ee, "ui/box_a");
+   fail_if(!obj);
+
+   Eina_List *lst_before = edje_edit_vg_trees_list_get(obj);
+   unsigned int before = eina_list_count(lst_before);
+   edje_edit_string_list_free(lst_before);
+
+   /* Adding a new unique name must succeed. */
+   ck_assert(edje_edit_vg_tree_add(obj, "brand_new_tree") == EINA_TRUE);
+   ck_assert(edje_edit_vg_tree_exists(obj, "brand_new_tree") == EINA_TRUE);
+
+   Eina_List *lst_after = edje_edit_vg_trees_list_get(obj);
+   ck_assert_int_eq((int)eina_list_count(lst_after), (int)(before + 1));
+   edje_edit_string_list_free(lst_after);
+
+   /* Adding the same name again must fail. */
+   ck_assert(edje_edit_vg_tree_add(obj, "brand_new_tree") == EINA_FALSE);
+   ck_assert(edje_edit_vg_tree_add(obj, "tile_a") == EINA_FALSE);
+
+   evas_object_del(obj);
+   ecore_evas_free(ee);
+}
+END_TEST
+
+/* -----------------------------------------------------------------------
+ * edje_edit_vg_tree_del — refuses to remove in-use trees
+ * ----------------------------------------------------------------------- */
+START_TEST(test_vg_tree_del)
+{
+   Ecore_Evas *ee = ecore_evas_buffer_new(1, 1);
+   fail_if(!ee);
+   Evas_Object *obj = _edit_obj_new(ee, "ui/box_a");
+   fail_if(!obj);
+
+   /* tile_a is referenced by ui/box_a (loaded group) — del must refuse. */
+   ck_assert(edje_edit_vg_tree_del(obj, "tile_a") == EINA_FALSE);
+   ck_assert(edje_edit_vg_tree_exists(obj, "tile_a") == EINA_TRUE);
+
+   /* Non-existent name must also fail gracefully. */
+   ck_assert(edje_edit_vg_tree_del(obj, "does_not_exist") == EINA_FALSE);
+
+   /* Add a fresh tree and then delete it — must succeed. */
+   ck_assert(edje_edit_vg_tree_add(obj, "tmp_tree") == EINA_TRUE);
+   ck_assert(edje_edit_vg_tree_del(obj, "tmp_tree") == EINA_TRUE);
+   ck_assert(edje_edit_vg_tree_exists(obj, "tmp_tree") == EINA_FALSE);
+
+   evas_object_del(obj);
+   ecore_evas_free(ee);
+}
+END_TEST
+
+/* -----------------------------------------------------------------------
+ * edje_edit_vg_tree_del — index-fixup path coverage
+ *
+ * Adds two fresh trees (A at slot N, B at slot N+1), binds a description to B,
+ * deletes A, and asserts the binding now resolves correctly to B.
+ * This exercises the memmove + tree_id-- fixup loop that was not covered by
+ * test_vg_tree_del (which only deleted the last entry).
+ * ----------------------------------------------------------------------- */
+START_TEST(test_vg_tree_del_fixup)
+{
+   Ecore_Evas *ee = ecore_evas_buffer_new(1, 1);
+   fail_if(!ee);
+   Evas_Object *obj = _edit_obj_new(ee, "ui/box_a");
+   fail_if(!obj);
+
+   /* Add two fresh unreferenced trees. */
+   ck_assert(edje_edit_vg_tree_add(obj, "fixup_a") == EINA_TRUE);
+   ck_assert(edje_edit_vg_tree_add(obj, "fixup_b") == EINA_TRUE);
+
+   /* Bind the vg part default description to fixup_b. */
+   ck_assert(edje_edit_part_vector_tree_use_set(obj, "vg", "default", 0.0,
+                                                "fixup_b") == EINA_TRUE);
+
+   /* Confirm binding resolves to fixup_b before deletion. */
+   const char *bound = edje_edit_part_vector_tree_use_get(obj, "vg", "default", 0.0);
+   fail_if(!bound);
+   ck_assert_str_eq(bound, "fixup_b");
+   edje_edit_string_free(bound);
+
+   /* Delete fixup_a (comes before fixup_b in the directory array).
+    * The fixup loop must decrement the tree_id stored in the vg description. */
+   ck_assert(edje_edit_vg_tree_del(obj, "fixup_a") == EINA_TRUE);
+   ck_assert(edje_edit_vg_tree_exists(obj, "fixup_a") == EINA_FALSE);
+   ck_assert(edje_edit_vg_tree_exists(obj, "fixup_b") == EINA_TRUE);
+
+   /* The binding must still resolve to fixup_b despite the index shift. */
+   bound = edje_edit_part_vector_tree_use_get(obj, "vg", "default", 0.0);
+   fail_if(!bound);
+   ck_assert_str_eq(bound, "fixup_b");
+   edje_edit_string_free(bound);
+
+   evas_object_del(obj);
+   ecore_evas_free(ee);
+}
+END_TEST
+
+/* -----------------------------------------------------------------------
+ * edje_edit_part_vector_tree_use_get/set
+ * ----------------------------------------------------------------------- */
+START_TEST(test_part_vector_tree_use_get_set)
+{
+   Ecore_Evas *ee = ecore_evas_buffer_new(1, 1);
+   fail_if(!ee);
+   Evas_Object *obj = _edit_obj_new(ee, "ui/box_a");
+   fail_if(!obj);
+
+   /* The default description uses tile_a. */
+   const char *name = edje_edit_part_vector_tree_use_get(obj, "vg", "default", 0.0);
+   fail_if(!name);
+   ck_assert_str_eq(name, "tile_a");
+   edje_edit_string_free(name);
+
+   /* Switch to tile_b. */
+   ck_assert(edje_edit_part_vector_tree_use_set(obj, "vg", "default", 0.0, "tile_b")
+             == EINA_TRUE);
+   name = edje_edit_part_vector_tree_use_get(obj, "vg", "default", 0.0);
+   fail_if(!name);
+   ck_assert_str_eq(name, "tile_b");
+   edje_edit_string_free(name);
+
+   /* Clear binding via NULL. */
+   ck_assert(edje_edit_part_vector_tree_use_set(obj, "vg", "default", 0.0, NULL)
+             == EINA_TRUE);
+   name = edje_edit_part_vector_tree_use_get(obj, "vg", "default", 0.0);
+   ck_assert(name == NULL);
+
+   /* Non-existent tree name must fail. */
+   ck_assert(edje_edit_part_vector_tree_use_set(obj, "vg", "default", 0.0, "ghost_tree")
+             == EINA_FALSE);
+
+   evas_object_del(obj);
+   ecore_evas_free(ee);
+}
+END_TEST
+
+/* -----------------------------------------------------------------------
+ * edje_edit_part_vector_overrides_list_get
+ * ----------------------------------------------------------------------- */
+START_TEST(test_part_vector_overrides_list_get)
+{
+   Ecore_Evas *ee = ecore_evas_buffer_new(1, 1);
+   fail_if(!ee);
+   /* box_a_hover's hover state has one override on "body". */
+   Evas_Object *obj = _edit_obj_new(ee, "ui/box_a_hover");
+   fail_if(!obj);
+
+   Eina_List *lst = edje_edit_part_vector_overrides_list_get(obj, "vg", "hover", 0.0);
+   ck_assert_int_eq((int)eina_list_count(lst), 1);
+   ck_assert_str_eq(eina_list_data_get(lst), "body");
+   edje_edit_string_list_free(lst);
+
+   /* Default state has no overrides. */
+   lst = edje_edit_part_vector_overrides_list_get(obj, "vg", "default", 0.0);
+   ck_assert_int_eq((int)eina_list_count(lst), 0);
+   edje_edit_string_list_free(lst);
+
+   evas_object_del(obj);
+   ecore_evas_free(ee);
+}
+END_TEST
+
+/* -----------------------------------------------------------------------
+ * edje_edit_part_vector_override_add / _del
+ * ----------------------------------------------------------------------- */
+START_TEST(test_part_vector_override_add_del)
+{
+   Ecore_Evas *ee = ecore_evas_buffer_new(1, 1);
+   fail_if(!ee);
+   Evas_Object *obj = _edit_obj_new(ee, "ui/box_a");
+   fail_if(!obj);
+
+   Eina_List *lst = edje_edit_part_vector_overrides_list_get(obj, "vg", "default", 0.0);
+   int before = (int)eina_list_count(lst);
+   edje_edit_string_list_free(lst);
+
+   /* Add a new override. */
+   ck_assert(edje_edit_part_vector_override_add(obj, "vg", "default", 0.0, "new_target")
+             == EINA_TRUE);
+   lst = edje_edit_part_vector_overrides_list_get(obj, "vg", "default", 0.0);
+   ck_assert_int_eq((int)eina_list_count(lst), before + 1);
+   edje_edit_string_list_free(lst);
+
+   /* Duplicate add must fail. */
+   ck_assert(edje_edit_part_vector_override_add(obj, "vg", "default", 0.0, "new_target")
+             == EINA_FALSE);
+
+   /* Delete it. */
+   ck_assert(edje_edit_part_vector_override_del(obj, "vg", "default", 0.0, "new_target")
+             == EINA_TRUE);
+   lst = edje_edit_part_vector_overrides_list_get(obj, "vg", "default", 0.0);
+   ck_assert_int_eq((int)eina_list_count(lst), before);
+   edje_edit_string_list_free(lst);
+
+   /* Delete non-existent must fail. */
+   ck_assert(edje_edit_part_vector_override_del(obj, "vg", "default", 0.0, "ghost")
+             == EINA_FALSE);
+
+   evas_object_del(obj);
+   ecore_evas_free(ee);
+}
+END_TEST
+
+/* -----------------------------------------------------------------------
+ * edje_edit_vector_node_color_class_get/set
+ * ----------------------------------------------------------------------- */
+START_TEST(test_vector_node_color_class_get_set)
+{
+   Ecore_Evas *ee = ecore_evas_buffer_new(1, 1);
+   fail_if(!ee);
+   Evas_Object *obj = _edit_obj_new(ee, "ui/box_a");
+   fail_if(!obj);
+
+   /* tile_a/body has no color_class by default. */
+   const char *cls = edje_edit_vector_node_color_class_get(obj, "tile_a", "body");
+   ck_assert(cls == NULL);
+
+   /* Set a class. */
+   ck_assert(edje_edit_vector_node_color_class_set(obj, "tile_a", "body", "my_class")
+             == EINA_TRUE);
+   cls = edje_edit_vector_node_color_class_get(obj, "tile_a", "body");
+   fail_if(!cls);
+   ck_assert_str_eq(cls, "my_class");
+   edje_edit_string_free(cls);
+
+   /* Clear via NULL. */
+   ck_assert(edje_edit_vector_node_color_class_set(obj, "tile_a", "body", NULL)
+             == EINA_TRUE);
+   cls = edje_edit_vector_node_color_class_get(obj, "tile_a", "body");
+   ck_assert(cls == NULL);
+
+   /* tile_classed/body already has fill.color_class set via EDC but not the
+    * node-level color_class — test the already-classed tree as a sanity check. */
+   ck_assert(edje_edit_vector_node_color_class_set(obj, "tile_classed", "body", "extra_class")
+             == EINA_TRUE);
+   cls = edje_edit_vector_node_color_class_get(obj, "tile_classed", "body");
+   fail_if(!cls);
+   ck_assert_str_eq(cls, "extra_class");
+   edje_edit_string_free(cls);
+
+   /* Unknown tree must fail. */
+   ck_assert(edje_edit_vector_node_color_class_set(obj, "no_tree", "body", "x")
+             == EINA_FALSE);
+   /* Unknown node must fail. */
+   ck_assert(edje_edit_vector_node_color_class_set(obj, "tile_a", "no_node", "x")
+             == EINA_FALSE);
+
+   evas_object_del(obj);
+   ecore_evas_free(ee);
+}
+END_TEST
+
+/* -----------------------------------------------------------------------
+ * Round-trip: add tree, set binding, save, reload, verify
+ * ----------------------------------------------------------------------- */
+START_TEST(test_vg_edit_round_trip)
+{
+   Ecore_Evas *ee = ecore_evas_buffer_new(1, 1);
+   fail_if(!ee);
+
+   /* Work on a temporary copy to avoid corrupting the test fixture. */
+   char tmp[] = "/tmp/edje_test_edit_vector_XXXXXX.edj";
+   int fd = mkstemps(tmp, 4);
+   fail_if(fd < 0);
+   close(fd);
+
+   {
+      /* Copy fixture -> tmp using ecore_file_cp. */
+      const char *src = "" "/data/test_vector_states.edj";
+      FILE *in = fopen(src, "rb");
+      fail_if(!in);
+      FILE *out = fopen(tmp, "wb");
+      fail_if(!out);
+      char buf[4096];
+      size_t n;
+      while ((n = fread(buf, 1, sizeof(buf), in)) > 0)
+        fwrite(buf, 1, n, out);
+      fclose(in);
+      fclose(out);
+   }
+
+   Evas_Object *obj = edje_edit_object_add(ecore_evas_get(ee));
+   fail_if(!obj);
+   fail_unless(edje_object_file_set(obj, tmp, "ui/box_a"));
+
+   /* Add a new tree entry. */
+   ck_assert(edje_edit_vg_tree_add(obj, "rt_tree") == EINA_TRUE);
+
+   /* Bind it to the default state. */
+   ck_assert(edje_edit_part_vector_tree_use_set(obj, "vg", "default", 0.0, "tile_b")
+             == EINA_TRUE);
+
+   /* Save. */
+   ck_assert(edje_edit_save(obj) == EINA_TRUE);
+   evas_object_del(obj);
+
+   /* Reload and verify. */
+   obj = edje_edit_object_add(ecore_evas_get(ee));
+   fail_if(!obj);
+   fail_unless(edje_object_file_set(obj, tmp, "ui/box_a"));
+
+   ck_assert(edje_edit_vg_tree_exists(obj, "rt_tree") == EINA_TRUE);
+
+   const char *bound = edje_edit_part_vector_tree_use_get(obj, "vg", "default", 0.0);
+   fail_if(!bound);
+   ck_assert_str_eq(bound, "tile_b");
+   edje_edit_string_free(bound);
+
+   evas_object_del(obj);
+   ecore_evas_free(ee);
+   unlink(tmp);
+}
+END_TEST
+
+void
+edje_test_edit_vector(TCase *tc)
+{
+   tcase_add_test(tc, test_vg_trees_list_get);
+   tcase_add_test(tc, test_vg_tree_exists);
+   tcase_add_test(tc, test_vg_tree_add);
+   tcase_add_test(tc, test_vg_tree_del);
+   tcase_add_test(tc, test_vg_tree_del_fixup);
+   tcase_add_test(tc, test_part_vector_tree_use_get_set);
+   tcase_add_test(tc, test_part_vector_overrides_list_get);
+   tcase_add_test(tc, test_part_vector_override_add_del);
+   tcase_add_test(tc, test_vector_node_color_class_get_set);
+   tcase_add_test(tc, test_vg_edit_round_trip);
+}
diff --git a/src/tests/edje/meson.build b/src/tests/edje/meson.build
index 709a387316..4f69a8ec86 100644
--- a/src/tests/edje/meson.build
+++ b/src/tests/edje/meson.build
@@ -11,6 +11,7 @@ edje_suite_src = [
   'edje_test_swallow.c',
   'edje_test_text.c',
   'edje_test_vector_states.c',
+  'edje_test_edit_vector.c',
   'efl_test_layout.c',
   'efl_test_layout2.c',
   'efl_test_layout3.c',

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

Reply via email to