2007-10-18 Emmanuele Bassi <ebassi@openedhand.com>

* clutter/clutter-script-private.h:
	* clutter/clutter-script.h:
	* clutter/clutter-script.c: Allow id-less objects: as long
	as they have a "type" member, a unique id will be provided.

	(json_object_end): Add merge id to the object information
	structure.

	(apply_behaviours), (add_children): Keep the unresolved
	objects around.

	(construct_stage), (clutter_script_construct_object): If an
	object has unresolved children or behaviours try resolving
	them when we ask for it.

	(json_parse_end), (clutter_script_ensure_objects): Ensure
	that the objects are fully constructed as best as we can when
	finished parsing.

	(object_info_free), (remove_by_merge_id):
	(clutter_script_unmerge_objects): Remove objects under the
	same merge id returned by the loading functions. (Fixes
	bug #558)
This commit is contained in:
Emmanuele Bassi 2007-10-18 12:31:07 +00:00
parent 1ccbe04505
commit f1105807fb
8 changed files with 315 additions and 124 deletions

View File

@ -1,3 +1,29 @@
2007-10-18 Emmanuele Bassi <ebassi@openedhand.com>
* clutter/clutter-script-private.h:
* clutter/clutter-script.h:
* clutter/clutter-script.c: Allow id-less objects: as long
as they have a "type" member, a unique id will be provided.
(json_object_end): Add merge id to the object information
structure.
(apply_behaviours), (add_children): Keep the unresolved
objects around.
(construct_stage), (clutter_script_construct_object): If an
object has unresolved children or behaviours try resolving
them when we ask for it.
(json_parse_end), (clutter_script_ensure_objects): Ensure
that the objects are fully constructed as best as we can when
finished parsing.
(object_info_free), (remove_by_merge_id):
(clutter_script_unmerge_objects): Remove objects under the
same merge id returned by the loading functions. (Fixes
bug #558)
2007-10-18 Matthew Allum <mallum@openedhand.com>
* clutter/clutter-score.c:

View File

@ -45,7 +45,11 @@ typedef struct {
GType gtype;
GObject *object;
guint is_toplevel : 1;
guint merge_id;
guint is_toplevel : 1;
guint has_unresolved : 1;
guint is_unmerged : 1;
} ObjectInfo;
typedef struct {

View File

@ -145,6 +145,7 @@ struct _ClutterScriptPrivate
GHashTable *objects;
guint last_merge_id;
guint last_unknown;
JsonParser *parser;
@ -632,10 +633,24 @@ json_object_end (JsonParser *parser,
JsonNode *val;
GList *members, *l;
/* ignore any non-named object */
if (!json_object_has_member (object, "id"))
return;
{
gchar *fake;
if (!json_object_has_member (object, "type"))
return;
fake = g_strdup_printf ("script-%d-%d",
priv->last_merge_id,
priv->last_unknown++);
val = json_node_new (JSON_NODE_VALUE);
json_node_set_string (val, fake);
json_object_add_member (object, "id", val);
g_free (fake);
}
if (!json_object_has_member (object, "type"))
{
val = json_object_get_member (object, "id");
@ -645,6 +660,7 @@ json_object_end (JsonParser *parser,
}
oinfo = g_slice_new0 (ObjectInfo);
oinfo->merge_id = priv->last_merge_id;
val = json_object_get_member (object, "id");
oinfo->id = json_node_dup_string (val);
@ -654,13 +670,14 @@ json_object_end (JsonParser *parser,
if (json_object_has_member (object, "type_func"))
{
/* remove "type_func", as it's not usef by anything else */
/* remove "type_func", as it's not used by anything else */
val = json_object_get_member (object, "type_func");
oinfo->type_func = json_node_dup_string (val);
json_object_remove_member (object, "type_func");
}
oinfo->is_toplevel = FALSE;
oinfo->is_unmerged = FALSE;
members = json_object_get_members (object);
for (l = members; l; l = l->next)
@ -686,9 +703,10 @@ json_object_end (JsonParser *parser,
}
g_list_free (members);
CLUTTER_NOTE (SCRIPT, "Added object `%s' (type:%s) with %d properties",
CLUTTER_NOTE (SCRIPT, "Added object `%s' (type:%s, id:%d) with %d properties",
oinfo->id,
oinfo->class_name,
oinfo->merge_id,
g_list_length (oinfo->properties));
g_hash_table_replace (priv->objects, g_strdup (oinfo->id), oinfo);
@ -932,12 +950,13 @@ translate_properties (ClutterScript *script,
static void
apply_behaviours (ClutterScript *script,
ClutterActor *actor,
GList *behaviours)
ObjectInfo *oinfo)
{
GObject *object;
GList *l;
GList *l, *unresolved;
for (l = behaviours; l != NULL; l = l->next)
unresolved = NULL;
for (l = oinfo->behaviours; l != NULL; l = l->next)
{
const gchar *name = l->data;
@ -949,8 +968,12 @@ apply_behaviours (ClutterScript *script,
oinfo = g_hash_table_lookup (script->priv->objects, name);
if (oinfo)
object = clutter_script_construct_object (script, oinfo);
else
continue;
}
if (!object)
{
unresolved = g_list_prepend (unresolved, g_strdup (name));
continue;
}
CLUTTER_NOTE (SCRIPT, "Applying behaviour `%s' to actor of type `%s'",
@ -959,17 +982,23 @@ apply_behaviours (ClutterScript *script,
clutter_behaviour_apply (CLUTTER_BEHAVIOUR (object), actor);
}
g_list_foreach (oinfo->behaviours, (GFunc) g_free, NULL);
g_list_free (oinfo->behaviours);
oinfo->behaviours = unresolved;
}
static void
add_children (ClutterScript *script,
ClutterContainer *container,
GList *children)
ObjectInfo *oinfo)
{
GObject *object;
GList *l;
GList *l, *unresolved;
for (l = children; l != NULL; l = l->next)
unresolved = NULL;
for (l = oinfo->children; l != NULL; l = l->next)
{
const gchar *name = l->data;
@ -981,8 +1010,12 @@ add_children (ClutterScript *script,
oinfo = g_hash_table_lookup (script->priv->objects, name);
if (oinfo)
object = clutter_script_construct_object (script, oinfo);
else
continue;
}
if (!object)
{
unresolved = g_list_prepend (unresolved, g_strdup (name));
continue;
}
CLUTTER_NOTE (SCRIPT, "Adding children `%s' to actor of type `%s'",
@ -991,6 +1024,11 @@ add_children (ClutterScript *script,
clutter_container_add_actor (container, CLUTTER_ACTOR (object));
}
g_list_foreach (oinfo->children, (GFunc) g_free, NULL);
g_list_free (oinfo->children);
oinfo->children = unresolved;
}
static GObject *
@ -1000,51 +1038,51 @@ construct_stage (ClutterScript *script,
GObjectClass *oclass = g_type_class_ref (CLUTTER_TYPE_STAGE);
GList *l;
if (oinfo->object)
if (oinfo->object && !oinfo->has_unresolved)
return oinfo->object;
oinfo->object = G_OBJECT (clutter_stage_get_default ());
for (l = oinfo->properties; l; l = l->next)
if (!oinfo->object)
{
PropertyInfo *pinfo = l->data;
const gchar *name = pinfo->property_name;
GParamSpec *pspec;
GValue value = { 0, };
oinfo->object = G_OBJECT (clutter_stage_get_default ());
pspec = g_object_class_find_property (oclass, name);
if (!pspec)
for (l = oinfo->properties; l; l = l->next)
{
g_warning ("Unknown property `%s' for class `ClutterStage'",
name);
continue;
PropertyInfo *pinfo = l->data;
const gchar *name = pinfo->property_name;
GParamSpec *pspec;
GValue value = { 0, };
pspec = g_object_class_find_property (oclass, name);
if (!pspec)
{
g_warning ("Unknown property `%s' for class `ClutterStage'",
name);
continue;
}
if (!translate_property (script, G_PARAM_SPEC_VALUE_TYPE (pspec),
name,
&pinfo->value,
&value))
{
g_warning ("Unable to set property `%s' for class `%s'",
pinfo->property_name,
g_type_name (oinfo->gtype));
continue;
}
g_object_set_property (oinfo->object, name, &value);
g_value_unset (&value);
}
if (!translate_property (script, G_PARAM_SPEC_VALUE_TYPE (pspec),
name,
&pinfo->value,
&value))
{
g_warning ("Unable to set property `%s' for class `%s'",
pinfo->property_name,
g_type_name (oinfo->gtype));
continue;
}
g_object_set_property (oinfo->object, name, &value);
g_value_unset (&value);
g_type_class_unref (oclass);
}
g_type_class_unref (oclass);
/* we know ClutterStage is a ClutterContainer */
if (oinfo->children)
{
/* we know ClutterStage is a ClutterContainer */
add_children (script,
CLUTTER_CONTAINER (oinfo->object),
oinfo->children);
}
add_children (script, CLUTTER_CONTAINER (oinfo->object), oinfo);
oinfo->has_unresolved = (oinfo->children != NULL);
g_object_set_data_full (oinfo->object, "clutter-script-name",
g_strdup (oinfo->id),
@ -1060,7 +1098,7 @@ clutter_script_construct_object (ClutterScript *script,
guint n_params, i;
GParameter *params;
if (oinfo->object)
if (oinfo->object && !oinfo->has_unresolved)
return oinfo->object;
if (oinfo->gtype == G_TYPE_INVALID)
@ -1081,45 +1119,50 @@ clutter_script_construct_object (ClutterScript *script,
if (g_type_is_a (oinfo->gtype, CLUTTER_TYPE_STAGE))
return construct_stage (script, oinfo);
params = NULL;
translate_properties (script, oinfo, &n_params, &params);
CLUTTER_NOTE (SCRIPT, "Creating instance for type `%s' (params:%d)",
g_type_name (oinfo->gtype),
n_params);
oinfo->object = g_object_newv (oinfo->gtype, n_params, params);
for (i = 0; i < n_params; i++)
if (!oinfo->object)
{
GParameter param = params[i];
g_value_unset (&param.value);
params = NULL;
translate_properties (script, oinfo, &n_params, &params);
CLUTTER_NOTE (SCRIPT, "Creating instance for type `%s' (params:%d)",
g_type_name (oinfo->gtype),
n_params);
oinfo->object = g_object_newv (oinfo->gtype, n_params, params);
for (i = 0; i < n_params; i++)
{
GParameter param = params[i];
g_value_unset (&param.value);
}
g_free (params);
if (CLUTTER_IS_BEHAVIOUR (oinfo->object) ||
CLUTTER_IS_TIMELINE (oinfo->object))
oinfo->is_toplevel = TRUE;
if (oinfo->id)
g_object_set_data_full (oinfo->object, "clutter-script-name",
g_strdup (oinfo->id),
g_free);
}
g_free (params);
if (CLUTTER_IS_CONTAINER (oinfo->object) && oinfo->children)
add_children (script, CLUTTER_CONTAINER (oinfo->object), oinfo->children);
add_children (script, CLUTTER_CONTAINER (oinfo->object), oinfo);
if (CLUTTER_IS_ACTOR (oinfo->object) && oinfo->behaviours)
apply_behaviours (script, CLUTTER_ACTOR (oinfo->object), oinfo->behaviours);
apply_behaviours (script, CLUTTER_ACTOR (oinfo->object), oinfo);
if (CLUTTER_IS_BEHAVIOUR (oinfo->object) ||
CLUTTER_IS_TIMELINE (oinfo->object))
oinfo->is_toplevel = TRUE;
if (oinfo->id)
g_object_set_data_full (oinfo->object, "clutter-script-name",
g_strdup (oinfo->id),
g_free);
oinfo->has_unresolved = (oinfo->children || oinfo->behaviours);
return oinfo->object;
}
static void
for_each_object (gpointer key,
gpointer value,
gpointer data)
construct_each_object (gpointer key,
gpointer value,
gpointer data)
{
ClutterScript *script = data;
ObjectInfo *oinfo = value;
@ -1134,7 +1177,7 @@ json_parse_end (JsonParser *parser,
ClutterScript *script = user_data;
ClutterScriptPrivate *priv = script->priv;
g_hash_table_foreach (priv->objects, for_each_object, script);
g_hash_table_foreach (priv->objects, construct_each_object, script);
}
static void
@ -1166,7 +1209,16 @@ object_info_free (gpointer data)
g_list_free (oinfo->behaviours);
if (oinfo->is_toplevel && oinfo->object)
g_object_unref (oinfo->object);
{
g_object_unref (oinfo->object);
oinfo->object = NULL;
}
if (oinfo->is_unmerged && oinfo->object)
{
clutter_actor_destroy (CLUTTER_ACTOR (oinfo->object));
oinfo->object = NULL;
}
g_slice_free (ObjectInfo, oinfo);
}
@ -1247,7 +1299,8 @@ clutter_script_new (void)
* the currently loaded ones, if any.
*
* Return value: on error, zero is returned and @error is set
* accordingly. On success, a positive integer is returned.
* accordingly. On success, the merge id for the UI definitions is
* returned. You can use the merge id with clutter_script_unmerge().
*
* Since: 0.6
*/
@ -1267,16 +1320,16 @@ clutter_script_load_from_file (ClutterScript *script,
g_free (priv->filename);
priv->filename = g_strdup (filename);
priv->is_filename = TRUE;
priv->last_merge_id += 1;
internal_error = NULL;
json_parser_load_from_file (priv->parser, filename, &internal_error);
if (internal_error)
{
g_propagate_error (error, internal_error);
priv->last_merge_id -= 1;
return 0;
}
else
priv->last_merge_id += 1;
return priv->last_merge_id;
}
@ -1293,7 +1346,8 @@ clutter_script_load_from_file (ClutterScript *script,
* the currently loaded ones, if any.
*
* Return value: on error, zero is returned and @error is set
* accordingly. On success, a positive integer is returned.
* accordingly. On success, the merge id for the UI definitions is
* returned. You can use the merge id with clutter_script_unmerge().
*
* Since: 0.6
*/
@ -1314,16 +1368,16 @@ clutter_script_load_from_data (ClutterScript *script,
g_free (priv->filename);
priv->filename = NULL;
priv->is_filename = FALSE;
priv->last_merge_id += 1;
internal_error = NULL;
json_parser_load_from_data (priv->parser, data, length, &internal_error);
if (internal_error)
{
g_propagate_error (error, internal_error);
priv->last_merge_id -= 1;
return 0;
}
else
priv->last_merge_id += 1;
return priv->last_merge_id;
}
@ -1409,6 +1463,84 @@ clutter_script_get_objects (ClutterScript *script,
return retval;
}
typedef struct {
ClutterScript *script;
guint merge_id;
GSList *ids;
} UnmergeData;
static void
remove_by_merge_id (gpointer key,
gpointer value,
gpointer data)
{
gchar *name = key;
ObjectInfo *oinfo = value;
UnmergeData *unmerge_data = data;
if (oinfo->merge_id == unmerge_data->merge_id)
{
unmerge_data->ids = g_slist_prepend (unmerge_data->ids, g_strdup (name));
oinfo->is_unmerged = TRUE;
}
}
/**
* clutter_script_unmerge_objects:
* @script: a #ClutterScript
* @merge_id: merge id returned when loading a UI definition
*
* Unmerges the objects identified by @merge_id.
*
* Since: 0.6
*/
void
clutter_script_unmerge_objects (ClutterScript *script,
guint merge_id)
{
ClutterScriptPrivate *priv;
UnmergeData data;
GSList *l;
g_return_if_fail (CLUTTER_IS_SCRIPT (script));
g_return_if_fail (merge_id > 0);
priv = script->priv;
data.script = script;
data.merge_id = merge_id;
data.ids = NULL;
g_hash_table_foreach (priv->objects, remove_by_merge_id, &data);
for (l = data.ids; l != NULL; l = l->next)
g_hash_table_remove (priv->objects, l->data);
g_slist_foreach (data.ids, (GFunc) g_free, NULL);
g_slist_free (data.ids);
clutter_script_ensure_objects (script);
}
/**
* clutter_script_ensure_object:
* @script: a #ClutterScript
*
* Ensure that every object defined inside @script is correctly
* constructed. You should rarely need to use this function.
*
* Since: 0.6
*/
void
clutter_script_ensure_objects (ClutterScript *script)
{
ClutterScriptPrivate *priv;
g_return_if_fail (CLUTTER_IS_SCRIPT (script));
priv = script->priv;
g_hash_table_foreach (priv->objects, construct_each_object, script);
}
gboolean
clutter_script_enum_from_string (GType type,
const gchar *string,
@ -1538,16 +1670,6 @@ clutter_script_flags_from_string (GType type,
return ret;
}
gboolean
clutter_script_value_from_data (ClutterScript *script,
GType gtype,
const gchar *data,
GValue *value,
GError **error)
{
return FALSE;
}
GQuark
clutter_script_error_quark (void)
{

View File

@ -82,21 +82,24 @@ struct _ClutterScriptClass
void (*_clutter_reserved8) (void);
};
GType clutter_script_get_type (void) G_GNUC_CONST;
GType clutter_script_get_type (void) G_GNUC_CONST;
ClutterScript *clutter_script_new (void);
guint clutter_script_load_from_file (ClutterScript *script,
const gchar *filename,
GError **error);
guint clutter_script_load_from_data (ClutterScript *script,
const gchar *data,
gsize length,
GError **error);
GObject * clutter_script_get_object (ClutterScript *script,
const gchar *name);
GList * clutter_script_get_objects (ClutterScript *script,
const gchar *first_name,
...) G_GNUC_NULL_TERMINATED;
ClutterScript *clutter_script_new (void);
guint clutter_script_load_from_file (ClutterScript *script,
const gchar *filename,
GError **error);
guint clutter_script_load_from_data (ClutterScript *script,
const gchar *data,
gsize length,
GError **error);
GObject * clutter_script_get_object (ClutterScript *script,
const gchar *name);
GList * clutter_script_get_objects (ClutterScript *script,
const gchar *first_name,
...) G_GNUC_NULL_TERMINATED;
void clutter_script_unmerge_objects (ClutterScript *script,
guint merge_id);
void clutter_script_ensure_objects (ClutterScript *script);
G_END_DECLS

View File

@ -1,3 +1,7 @@
2007-10-18 Emmanuele Bassi <ebassi@openedhand.com>
* clutter-sections.txt: Add new ClutterScript API.
2007-10-10 Emmanuele Bassi <ebassi@openedhand.com>
* clutter-sections.txt: Add new API and rearrange the subsections.

View File

@ -1098,6 +1098,8 @@ clutter_script_load_from_data
clutter_script_load_from_file
clutter_script_get_object
clutter_script_get_objects
clutter_script_unmerge_objects
clutter_script_ensure_objects
<SUBSECTION Standard>
CLUTTER_TYPE_SCRIPT
CLUTTER_SCRIPT

View File

@ -5,6 +5,22 @@
#include <clutter/clutter.h>
static ClutterScript *script = NULL;
static guint merge_id = 0;
static const gchar *test_unmerge =
"{"
" \"id\" : \"blue-button\","
" \"type\" : \"ClutterRectangle\","
" \"color\" : \"#0000ffff\","
" \"x\" : 350,"
" \"y\" : 50,"
" \"width\" : 100,"
" \"height\" : 100,"
" \"visible\" : true,"
" \"reactive\" : true"
"}";
static const gchar *test_behaviour =
"["
" {"
@ -37,11 +53,18 @@ static const gchar *test_behaviour =
" }"
"]";
static gboolean
blue_button_press (ClutterActor *actor,
ClutterButtonEvent *event,
gpointer data)
{
clutter_script_unmerge_objects (script, merge_id);
}
int
main (int argc, char *argv[])
{
ClutterScript *script;
GObject *stage, *timeline;
GObject *stage, *timeline, *blue_button;
GError *error = NULL;
clutter_init (&argc, &argv);
@ -69,9 +92,25 @@ main (int argc, char *argv[])
return EXIT_FAILURE;
}
merge_id = clutter_script_load_from_data (script, test_unmerge, -1, &error);
if (error)
{
g_print ("*** Error:\n"
"*** %s\n", error->message);
g_error_free (error);
g_object_unref (script);
return EXIT_FAILURE;
}
stage = clutter_script_get_object (script, "main-stage");
clutter_actor_show (CLUTTER_ACTOR (stage));
blue_button = clutter_script_get_object (script, "blue-button");
g_signal_connect (blue_button,
"button-press-event",
G_CALLBACK (blue_button_press),
NULL);
timeline = clutter_script_get_object (script, "main-timeline");
clutter_timeline_start (CLUTTER_TIMELINE (timeline));

View File

@ -26,17 +26,7 @@
"height" : 100,
"visible" : true,
"behaviours" : [ "fade-behaviour" ]
},
{
"id" : "blue-button",
"type" : "ClutterRectangle",
"color" : "#0000ffff",
"x" : 350,
"y" : 50,
"width" : 100,
"height" : 100,
"visible" : true,
},
},
{
"id" : "red-hand",
"type" : "ClutterTexture",
@ -46,7 +36,8 @@
"opacity" : 100,
"visible" : true,
"behaviours" : [ "rotate-behaviour", "fade-behaviour" ]
}
},
"blue-button"
]
}
}