diff --git a/.gitignore b/.gitignore index 4319cada8..af23dee34 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/clutter/clutter-script-parser.c b/clutter/clutter-script-parser.c index 7195f4793..5e93d8546 100644 --- a/clutter/clutter-script-parser.c +++ b/clutter/clutter-script-parser.c @@ -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; } @@ -1530,6 +1533,112 @@ clutter_script_construct_parameters (ClutterScript *script, return unparsed; } +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, @@ -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 diff --git a/clutter/clutter-script-private.h b/clutter/clutter-script-private.h index 88e804c17..5512f5e50 100644 --- a/clutter/clutter-script-private.h +++ b/clutter/clutter-script-private.h @@ -80,6 +80,7 @@ typedef struct { GParamSpec *pspec; guint is_child : 1; + guint is_layout : 1; } PropertyInfo; typedef struct { diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index 7b0aa6bce..e95c73db3 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -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); diff --git a/tests/conform/test-script-parser.c b/tests/conform/test-script-parser.c index 18a49c0f1..8e6c5ff67 100644 --- a/tests/conform/test-script-parser.c +++ b/tests/conform/test-script-parser.c @@ -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); +} diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 2dfca3e79..f886a6d6f 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -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 index 000000000..623916ad0 --- /dev/null +++ b/tests/data/test-script-layout-property.json @@ -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 + } + ] + } +]