script: Support layout manager properties
authorEmmanuele Bassi <ebassi@linux.intel.com>
Mon, 7 Jun 2010 21:45:34 +0000 (22:45 +0100)
committerEmmanuele Bassi <ebassi@linux.intel.com>
Mon, 7 Jun 2010 21:45:34 +0000 (22:45 +0100)
Layout properties work similarly to child properties, with the added
headache that they require the 3-tuple:

  ( layout manager, container, actor )

to be valid in order to be inspected, parsed and applied. This means
using the newly added back-pointer from the container to the layout
manager and then rejigging a bit how the ScriptParser handles the
unresolved properties.

Similarly to the child properties, which use the "child::" prefix, the
layout manager properties use the "layout::" prefix and are defined with
the child of a container holding a layout manager.

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

index 4319cad..af23dee 100644 (file)
@@ -264,6 +264,7 @@ TAGS
 /tests/conform/test-state-base
 /tests/conform/test-texture-pick-with-alpha
 /tests/conform/test-cogl-object
+/tests/conform/test-script-layout-property
 /tests/conform/wrappers
 /tests/micro-bench/test-text-perf
 /tests/micro-bench/test-text
index 7195f47..5e93d85 100644 (file)
@@ -1051,6 +1051,7 @@ clutter_script_parser_object_end (JsonParser *json_parser,
       pinfo->node = json_node_copy (node);
       pinfo->pspec = NULL;
       pinfo->is_child = g_str_has_prefix (name, "child::") ? TRUE : FALSE;
+      pinfo->is_layout = g_str_has_prefix (name, "layout::") ? TRUE : FALSE;
 
       oinfo->properties = g_list_prepend (oinfo->properties, pinfo);
     }
@@ -1422,9 +1423,11 @@ clutter_script_translate_parameters (ClutterScript  *script,
       GParameter param = { NULL };
       gboolean res = FALSE;
 
-      if (pinfo->is_child)
+      if (pinfo->is_child || pinfo->is_layout)
         {
-          CLUTTER_NOTE (SCRIPT, "Child property '%s' ignored", pinfo->name);
+          CLUTTER_NOTE (SCRIPT, "Skipping %s property '%s'",
+                        pinfo->is_child ? "child" : "layout",
+                        pinfo->name);
           unparsed = g_list_prepend (unparsed, pinfo);
           continue;
         }
@@ -1531,6 +1534,112 @@ clutter_script_construct_parameters (ClutterScript  *script,
 }
 
 static void
+apply_layout_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;
+  ClutterLayoutManager *manager;
+  GType meta_type;
+
+  manager = g_object_get_data (G_OBJECT (container), "clutter-layout-manager");
+  if (manager == NULL)
+    return;
+
+  meta_type = _clutter_layout_manager_get_child_meta_type (manager);
+  if (meta_type == G_TYPE_INVALID)
+    return;
+
+  CLUTTER_NOTE (SCRIPT, "Layout manager of type '%s' with meta type '%s'",
+                G_OBJECT_TYPE_NAME (manager),
+                g_type_name (meta_type));
+
+  /* shortcut, to avoid typechecking every time */
+  if (CLUTTER_IS_SCRIPTABLE (manager))
+    {
+      scriptable = CLUTTER_SCRIPTABLE (manager);
+      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, };
+      gboolean res = FALSE;
+      const gchar *name;
+
+      if (!pinfo->is_layout)
+        {
+          unresolved = g_list_prepend (unresolved, pinfo);
+          continue;
+        }
+
+      name = pinfo->name + strlen ("layout::");
+
+      pinfo->pspec =
+        clutter_layout_manager_find_child_property (manager, name);
+
+      if (pinfo->pspec != NULL)
+        g_param_spec_ref (pinfo->pspec);
+
+      CLUTTER_NOTE (SCRIPT, "Parsing %s layout 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, "Layout property '%s' ignored", name);
+          unresolved = g_list_prepend (unresolved, pinfo);
+          continue;
+        }
+
+      CLUTTER_NOTE (SCRIPT,
+                    "Setting %s layout 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_layout_manager_child_set_property (manager, container, actor,
+                                                 name,
+                                                 &value);
+
+      g_value_unset (&value);
+
+      property_info_free (pinfo);
+    }
+
+  g_list_free (properties);
+
+  oinfo->properties = unresolved;
+}
+
+static void
 apply_child_properties (ClutterScript    *script,
                         ClutterContainer *container,
                         ClutterActor     *actor,
@@ -1715,10 +1824,6 @@ 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);
@@ -1737,6 +1842,49 @@ _clutter_script_check_unresolved (ClutterScript *script,
   if (oinfo->behaviours != NULL && CLUTTER_IS_ACTOR (oinfo->object))
     apply_behaviours (script, oinfo);
 
+  /* this is a bit *eugh*, but it allows us to effectively make sure
+   * that child and layout properties are parsed and applied to the
+   * right child
+   */
+  if (oinfo->properties != NULL && CLUTTER_IS_ACTOR (oinfo->object))
+    {
+      ClutterActor *parent;
+
+      parent = clutter_actor_get_parent (CLUTTER_ACTOR (oinfo->object));
+      if (parent != NULL && CLUTTER_IS_CONTAINER (parent))
+        {
+          ClutterContainer *container = CLUTTER_CONTAINER (parent);
+          ClutterActor *actor = CLUTTER_ACTOR (oinfo->object);
+          GList *children, *l;
+
+          children = clutter_container_get_children (container);
+
+          for (l = children; l != NULL; l = l->next)
+            {
+              GObject *child = l->data;
+              ObjectInfo *child_info;
+              const gchar *id;
+
+              id = clutter_get_script_id (child);
+              if (id == NULL || *id == '\0')
+                continue;
+
+              child_info = _clutter_script_get_object_info (script, id);
+              if (child_info == NULL)
+                continue;
+
+              apply_child_properties (script, container,
+                                      actor,
+                                      child_info);
+              apply_layout_properties (script, container,
+                                       actor,
+                                       child_info);
+            }
+
+          g_list_free (children);
+        }
+    }
+
   if (oinfo->properties || oinfo->children || oinfo->behaviours)
     oinfo->has_unresolved = TRUE;
   else
index 88e804c..5512f5e 100644 (file)
@@ -80,6 +80,7 @@ typedef struct {
   GParamSpec *pspec;
 
   guint is_child : 1;
+  guint is_layout : 1;
 } PropertyInfo;
 
 typedef struct {
index 7b0aa6b..e95c73d 100644 (file)
@@ -186,6 +186,7 @@ main (int argc, char **argv)
   TEST_CONFORM_SIMPLE ("/script", test_animator_properties);
   TEST_CONFORM_SIMPLE ("/script", test_animator_multi_properties);
   TEST_CONFORM_SIMPLE ("/script", test_state_base);
+  TEST_CONFORM_SIMPLE ("/script", test_script_layout_property);
 
   TEST_CONFORM_SIMPLE ("/behaviours", test_behaviours);
 
index 18a49c0..8e6c5ff 100644 (file)
@@ -336,3 +336,54 @@ test_script_animation (TestConformSimpleFixture *fixture,
   g_object_unref (script);
   g_free (test_file);
 }
+
+void
+test_script_layout_property (TestConformSimpleFixture *fixture,
+                             gconstpointer dummy G_GNUC_UNUSED)
+{
+  ClutterScript *script = clutter_script_new ();
+  GObject *manager, *container, *actor;
+  GError *error = NULL;
+  gchar *test_file;
+  gboolean x_fill, expand;
+  ClutterBoxAlignment y_align;
+
+  test_file = clutter_test_get_data_file ("test-script-layout-property.json");
+  clutter_script_load_from_file (script, test_file, &error);
+  if (g_test_verbose () && error)
+    g_print ("Error: %s", error->message);
+
+#if GLIB_CHECK_VERSION (2, 20, 0)
+  g_assert_no_error (error);
+#else
+  g_assert (error == NULL);
+#endif
+
+  manager = container = actor = NULL;
+  clutter_script_get_objects (script,
+                              "manager", &manager,
+                              "container", &container,
+                              "actor", &actor,
+                              NULL);
+
+  g_assert (CLUTTER_IS_LAYOUT_MANAGER (manager));
+  g_assert (CLUTTER_IS_CONTAINER (container));
+  g_assert (CLUTTER_IS_ACTOR (actor));
+
+  x_fill = FALSE;
+  y_align = CLUTTER_BOX_ALIGNMENT_START;
+  expand = FALSE;
+  clutter_layout_manager_child_get (CLUTTER_LAYOUT_MANAGER (manager),
+                                    CLUTTER_CONTAINER (container),
+                                    CLUTTER_ACTOR (actor),
+                                    "x-fill", &x_fill,
+                                    "y-align", &y_align,
+                                    "expand", &expand,
+                                    NULL);
+
+  g_assert (x_fill);
+  g_assert (y_align == CLUTTER_BOX_ALIGNMENT_CENTER);
+  g_assert (expand);
+
+  g_object_unref (script);
+}
index 2dfca3e..f886a6d 100644 (file)
@@ -13,6 +13,7 @@ json_files = \
        test-animator-2.json                    \
        test-animator-3.json                    \
        test-state-1.json                       \
+       test-script-layout-property.json        \
        $(NULL)
 
 png_files = \
diff --git a/tests/data/test-script-layout-property.json b/tests/data/test-script-layout-property.json
new file mode 100644 (file)
index 0000000..623916a
--- /dev/null
@@ -0,0 +1,16 @@
+[
+  { "id" : "manager", "type" : "ClutterBoxLayout" },
+
+  {
+    "id" : "container", "type" : "ClutterBox",
+    "layout-manager" : "manager",
+    "children" : [
+      {
+        "id" : "actor", "type" : "ClutterRectangle",
+        "layout::x-fill" : true,
+        "layout::y-align" : "center",
+        "layout::expand" : true
+      }
+    ]
+  }
+]