script: Allow parsing child properties
authorEmmanuele Bassi <ebassi@linux.intel.com>
Wed, 4 Nov 2009 16:45:44 +0000 (16:45 +0000)
committerEmmanuele Bassi <ebassi@linux.intel.com>
Wed, 4 Nov 2009 16:50:35 +0000 (16:50 +0000)
The ClutterScript parser needs to be extended to parse child properties
and apply them after an actor has been added to a container. In order to
distinguish child properties from regular GObject properties we can use
the "child::" prefix, e.g.:

  {
    "type" : "ClutterRectangle",
    "id" : "child-01",
    "child::has-focus" : true,
    ...
  }

Parsing child properties can be deferred to the ClutterScriptable
interface, just like regular properties.

.gitignore
clutter/clutter-script-parser.c
clutter/clutter-script-private.h
tests/conform/Makefile.am
tests/conform/test-conform-main.c
tests/conform/test-script-parser.c
tests/data/test-script-child.json [new file with mode: 0644]

index 5b4e47a..ff5f3f8 100644 (file)
@@ -219,6 +219,7 @@ TAGS
 /tests/conform/test-cogl-viewport
 /tests/conform/test-texture-fbo
 /tests/conform/test-script-single
+/tests/conform/test-script-child
 /tests/micro-bench/test-text-perf
 /tests/micro-bench/test-text
 /tests/micro-bench/test-picking
index 84279ae..fc7172f 100644 (file)
@@ -1008,6 +1008,7 @@ clutter_script_parser_object_end (JsonParser *json_parser,
       pinfo->name = g_strdup (name);
       pinfo->node = json_node_copy (node);
       pinfo->pspec = NULL;
+      pinfo->is_child = g_str_has_prefix (name, "child::") ? TRUE : FALSE;
 
       oinfo->properties = g_list_prepend (oinfo->properties, pinfo);
     }
@@ -1352,6 +1353,13 @@ clutter_script_translate_parameters (ClutterScript  *script,
       GParameter param = { NULL };
       gboolean res = FALSE;
 
+      if (pinfo->is_child)
+        {
+          CLUTTER_NOTE (SCRIPT, "Child property '%s' ignored", pinfo->name);
+          unparsed = g_list_prepend (unparsed, pinfo);
+          continue;
+        }
+
       CLUTTER_NOTE (SCRIPT, "Parsing %s property (id:%s)",
                     pinfo->pspec ? "regular" : "custom",
                     pinfo->name);
@@ -1454,10 +1462,111 @@ clutter_script_construct_parameters (ClutterScript  *script,
 }
 
 static void
+apply_child_properties (ClutterScript    *script,
+                        ClutterContainer *container,
+                        ClutterActor     *actor,
+                        ObjectInfo       *oinfo)
+{
+  ClutterScriptable *scriptable = NULL;
+  ClutterScriptableIface *iface = NULL;
+  gboolean set_custom_property = FALSE;
+  gboolean parse_custom_node = FALSE;
+  GList *l, *unresolved, *properties;
+  GObjectClass *klass;
+  GType meta_type;
+
+  meta_type = CLUTTER_CONTAINER_GET_IFACE (container)->child_meta_type;
+  if (meta_type == G_TYPE_INVALID)
+    return;
+
+  klass = G_OBJECT_GET_CLASS (container);
+
+  /* shortcut, to avoid typechecking every time */
+  if (CLUTTER_IS_SCRIPTABLE (container))
+    {
+      scriptable = CLUTTER_SCRIPTABLE (container);
+      iface = CLUTTER_SCRIPTABLE_GET_IFACE (scriptable);
+
+      parse_custom_node = iface->parse_custom_node != NULL ? TRUE : FALSE;
+      set_custom_property = iface->set_custom_property != NULL ? TRUE : FALSE;
+    }
+
+  properties = oinfo->properties;
+  oinfo->properties = NULL;
+
+  unresolved = NULL;
+  for (l = properties; l != NULL; l = l->next)
+    {
+      PropertyInfo *pinfo = l->data;
+      GValue value = { 0, };
+      const gchar *name;
+      gboolean res;
+
+      if (!pinfo->is_child)
+        {
+          unresolved = g_list_prepend (unresolved, pinfo);
+          continue;
+        }
+
+      name = pinfo->name + strlen ("child::");
+
+      pinfo->pspec =
+        clutter_container_class_find_child_property (klass, name);
+
+      if (pinfo->pspec != NULL)
+        g_param_spec_ref (pinfo->pspec);
+
+      CLUTTER_NOTE (SCRIPT, "Parsing %s child property (id:%s)",
+                    pinfo->pspec != NULL ? "regular" : "custom",
+                    name);
+
+      if (parse_custom_node)
+        res = iface->parse_custom_node (scriptable, script, &value,
+                                        name,
+                                        pinfo->node);
+
+      if (!res)
+        res = clutter_script_parse_node (script, &value,
+                                         name,
+                                         pinfo->node,
+                                         pinfo->pspec);
+
+      if (!res)
+        {
+          CLUTTER_NOTE (SCRIPT, "Child property '%s' ignored", name);
+          unresolved = g_list_prepend (unresolved, pinfo);
+          continue;
+        }
+
+      
+      CLUTTER_NOTE (SCRIPT,
+                    "Setting %s child property '%s' (type:%s) to "
+                    "object '%s' (id:%s)",
+                    set_custom_property ? "custom" : "regular",
+                    name,
+                    g_type_name (G_VALUE_TYPE (&value)),
+                    g_type_name (oinfo->gtype),
+                    oinfo->id);
+
+      clutter_container_child_set_property (container, actor,
+                                            name,
+                                            &value);
+
+      g_value_unset (&value);
+
+      property_info_free (pinfo);
+    }
+
+  g_list_free (properties);
+
+  oinfo->properties = unresolved;
+}
+
+static void
 apply_behaviours (ClutterScript *script,
                   ObjectInfo    *oinfo)
 {
-  ClutterActor  *actor = CLUTTER_ACTOR (oinfo->object);
+  ClutterActor *actor = CLUTTER_ACTOR (oinfo->object);
   GList *l, *unresolved;
 
   unresolved = NULL;
@@ -1525,6 +1634,10 @@ add_children (ClutterScript *script,
                     g_type_name (G_OBJECT_TYPE (container)));
 
       clutter_container_add_actor (container, CLUTTER_ACTOR (object));
+
+      apply_child_properties (script,
+                              container, CLUTTER_ACTOR (object),
+                              child_info);
     }
 
   g_list_foreach (oinfo->children, (GFunc) g_free, NULL);
index 39325ab..b60bec7 100644 (file)
@@ -78,6 +78,8 @@ typedef struct {
   gchar *name;
   JsonNode *node;
   GParamSpec *pspec;
+
+  guint is_child : 1;
 } PropertyInfo;
 
 typedef struct {
index 7139ccd..1c56548 100644 (file)
@@ -88,6 +88,8 @@ test_conformance_CFLAGS = $(CLUTTER_CFLAGS) $(MAINTAINER_CFLAGS)
 
 test_conformance_LDADD = $(top_builddir)/clutter/libclutter-@CLUTTER_WINSYS@-@CLUTTER_API_VERSION@.la $(CLUTTER_LIBS)
 
+test_conformance_LDFLAGS = -rdynamic
+
 .PHONY: test
 .PHONY: test-report test-report-normal test-report-disable-npots
 .PHONY: full-report full-report-normal full-report-disable-npots
index e588ee7..9872e23 100644 (file)
@@ -186,6 +186,7 @@ main (int argc, char **argv)
   TEST_CONFORM_SIMPLE ("/cogl", test_cogl_readpixels);
 
   TEST_CONFORM_SIMPLE ("/script", test_script_single);
+  TEST_CONFORM_SIMPLE ("/script", test_script_child);
 
   return g_test_run ();
 }
index 395308a..6411fec 100644 (file)
@@ -5,6 +5,162 @@
 
 #include "test-conform-common.h"
 
+#define TEST_TYPE_GROUP                 (test_group_get_type ())
+#define TEST_TYPE_GROUP_META            (test_group_meta_get_type ())
+
+#define TEST_GROUP(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_GROUP, TestGroup))
+#define TEST_IS_GROUP(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_GROUP))
+
+#define TEST_GROUP_META(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_GROUP_META, TestGroupMeta))
+#define TEST_IS_GROUP_META(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_GROUP_META))
+
+typedef struct _ClutterGroup            TestGroup;
+
+typedef struct _TestGroupMeta {
+  ClutterChildMeta parent_instance;
+
+  guint is_focus : 1;
+} TestGroupMeta;
+
+typedef struct _ClutterGroupClass       TestGroupClass;
+typedef struct _ClutterChildMetaClass   TestGroupMetaClass;
+
+G_DEFINE_TYPE (TestGroupMeta, test_group_meta, CLUTTER_TYPE_CHILD_META);
+
+enum
+{
+  PROP_META_0,
+
+  PROP_META_FOCUS
+};
+
+static void
+test_group_meta_set_property (GObject      *gobject,
+                              guint         prop_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+  TestGroupMeta *self = TEST_GROUP_META (gobject);
+
+  switch (prop_id)
+    {
+    case PROP_META_FOCUS:
+      self->is_focus = g_value_get_boolean (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+test_group_meta_get_property (GObject    *gobject,
+                              guint       prop_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+  TestGroupMeta *self = TEST_GROUP_META (gobject);
+
+  switch (prop_id)
+    {
+    case PROP_META_FOCUS:
+      g_value_set_boolean (value, self->is_focus);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+test_group_meta_class_init (TestGroupMetaClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GParamSpec *pspec;
+
+  gobject_class->set_property = test_group_meta_set_property;
+  gobject_class->get_property = test_group_meta_get_property;
+
+  pspec = g_param_spec_boolean ("focus", "Focus", "Focus",
+                                FALSE,
+                                G_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class, PROP_META_FOCUS, pspec);
+}
+
+static void
+test_group_meta_init (TestGroupMeta *meta)
+{
+  meta->is_focus = FALSE;
+}
+
+static void
+clutter_container_iface_init (ClutterContainerIface *iface)
+{
+  iface->child_meta_type = TEST_TYPE_GROUP_META;
+}
+
+G_DEFINE_TYPE_WITH_CODE (TestGroup, test_group, CLUTTER_TYPE_GROUP,
+                         G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
+                                                clutter_container_iface_init));
+
+static void
+test_group_class_init (TestGroupClass *klass)
+{
+}
+
+static void
+test_group_init (TestGroup *self)
+{
+}
+
+void
+test_script_child (TestConformSimpleFixture *fixture,
+                   gconstpointer dummy)
+{
+  ClutterScript *script = clutter_script_new ();
+  GObject *container, *actor;
+  GError *error = NULL;
+  gboolean focus_ret;
+  gchar *test_file;
+
+  test_file = clutter_test_get_data_file ("test-script-child.json");
+  clutter_script_load_from_file (script, test_file, &error);
+  if (g_test_verbose () && (error != NULL))
+    g_warning ("Unable to load '%s': %s", test_file, error->message);
+  g_assert (error == NULL);
+
+  container = actor = NULL;
+  clutter_script_get_objects (script,
+                              "test-group", &container,
+                              "test-rect-1", &actor,
+                              NULL);
+  g_assert (TEST_IS_GROUP (container));
+  g_assert (CLUTTER_IS_RECTANGLE (actor));
+
+  focus_ret = FALSE;
+  clutter_container_child_get (CLUTTER_CONTAINER (container),
+                               CLUTTER_ACTOR (actor),
+                               "focus", &focus_ret,
+                               NULL);
+  g_assert (focus_ret);
+
+  actor = clutter_script_get_object (script, "test-rect-2");
+  g_assert (CLUTTER_IS_RECTANGLE (actor));
+
+  focus_ret = FALSE;
+  clutter_container_child_get (CLUTTER_CONTAINER (container),
+                               CLUTTER_ACTOR (actor),
+                               "focus", &focus_ret,
+                               NULL);
+  g_assert (!focus_ret);
+
+  g_object_unref (script);
+  clutter_actor_destroy (CLUTTER_ACTOR (container));
+  g_free (test_file);
+}
+
 void
 test_script_single (TestConformSimpleFixture *fixture,
                     gconstpointer dummy)
diff --git a/tests/data/test-script-child.json b/tests/data/test-script-child.json
new file mode 100644 (file)
index 0000000..5e8ac52
--- /dev/null
@@ -0,0 +1,21 @@
+{
+  "type" : "TestGroup",
+  "id" : "test-group",
+  "children" : [
+    {
+      "type" : "ClutterRectangle",
+      "id" : "test-rect-1",
+      "width" : 100.0,
+      "height" : 100.0,
+      "color" : [ 255, 0, 0, 255 ],
+      "child::focus" : true
+    },
+    {
+      "type" : "ClutterRectangle",
+      "id" : "test-rect-2",
+      "width" : 100.0,
+      "height" : 100.0,
+      "color" : [ 0, 255, 0, 255 ]
+    }
+  ]
+}