From 95d78acb4ca72951fc9552c9fcffa645b1be739a Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 4 Nov 2009 16:45:44 +0000 Subject: [PATCH] script: Allow parsing child properties 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 | 1 + clutter/clutter-script-parser.c | 115 ++++++++++++++++++++++++++- clutter/clutter-script-private.h | 2 + tests/conform/Makefile.am | 2 + tests/conform/test-conform-main.c | 1 + tests/conform/test-script-parser.c | 156 +++++++++++++++++++++++++++++++++++++ tests/data/test-script-child.json | 21 +++++ 7 files changed, 297 insertions(+), 1 deletion(-) create mode 100644 tests/data/test-script-child.json diff --git a/.gitignore b/.gitignore index 5b4e47a..ff5f3f8 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/clutter/clutter-script-parser.c b/clutter/clutter-script-parser.c index 84279ae..fc7172f 100644 --- a/clutter/clutter-script-parser.c +++ b/clutter/clutter-script-parser.c @@ -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); diff --git a/clutter/clutter-script-private.h b/clutter/clutter-script-private.h index 39325ab..b60bec7 100644 --- a/clutter/clutter-script-private.h +++ b/clutter/clutter-script-private.h @@ -78,6 +78,8 @@ typedef struct { gchar *name; JsonNode *node; GParamSpec *pspec; + + guint is_child : 1; } PropertyInfo; typedef struct { diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am index 7139ccd..1c56548 100644 --- a/tests/conform/Makefile.am +++ b/tests/conform/Makefile.am @@ -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 diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index e588ee7..9872e23 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 ("/cogl", test_cogl_readpixels); TEST_CONFORM_SIMPLE ("/script", test_script_single); + TEST_CONFORM_SIMPLE ("/script", test_script_child); return g_test_run (); } diff --git a/tests/conform/test-script-parser.c b/tests/conform/test-script-parser.c index 395308a..6411fec 100644 --- a/tests/conform/test-script-parser.c +++ b/tests/conform/test-script-parser.c @@ -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 index 0000000..5e8ac52 --- /dev/null +++ b/tests/data/test-script-child.json @@ -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 ] + } + ] +} -- 2.7.4