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> 2007-10-18 Matthew Allum <mallum@openedhand.com>
* clutter/clutter-score.c: * clutter/clutter-score.c:

View File

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

View File

@ -145,6 +145,7 @@ struct _ClutterScriptPrivate
GHashTable *objects; GHashTable *objects;
guint last_merge_id; guint last_merge_id;
guint last_unknown;
JsonParser *parser; JsonParser *parser;
@ -632,10 +633,24 @@ json_object_end (JsonParser *parser,
JsonNode *val; JsonNode *val;
GList *members, *l; GList *members, *l;
/* ignore any non-named object */
if (!json_object_has_member (object, "id")) if (!json_object_has_member (object, "id"))
{
gchar *fake;
if (!json_object_has_member (object, "type"))
return; 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")) if (!json_object_has_member (object, "type"))
{ {
val = json_object_get_member (object, "id"); val = json_object_get_member (object, "id");
@ -645,6 +660,7 @@ json_object_end (JsonParser *parser,
} }
oinfo = g_slice_new0 (ObjectInfo); oinfo = g_slice_new0 (ObjectInfo);
oinfo->merge_id = priv->last_merge_id;
val = json_object_get_member (object, "id"); val = json_object_get_member (object, "id");
oinfo->id = json_node_dup_string (val); oinfo->id = json_node_dup_string (val);
@ -654,13 +670,14 @@ json_object_end (JsonParser *parser,
if (json_object_has_member (object, "type_func")) 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"); val = json_object_get_member (object, "type_func");
oinfo->type_func = json_node_dup_string (val); oinfo->type_func = json_node_dup_string (val);
json_object_remove_member (object, "type_func"); json_object_remove_member (object, "type_func");
} }
oinfo->is_toplevel = FALSE; oinfo->is_toplevel = FALSE;
oinfo->is_unmerged = FALSE;
members = json_object_get_members (object); members = json_object_get_members (object);
for (l = members; l; l = l->next) for (l = members; l; l = l->next)
@ -686,9 +703,10 @@ json_object_end (JsonParser *parser,
} }
g_list_free (members); 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->id,
oinfo->class_name, oinfo->class_name,
oinfo->merge_id,
g_list_length (oinfo->properties)); g_list_length (oinfo->properties));
g_hash_table_replace (priv->objects, g_strdup (oinfo->id), oinfo); g_hash_table_replace (priv->objects, g_strdup (oinfo->id), oinfo);
@ -932,12 +950,13 @@ translate_properties (ClutterScript *script,
static void static void
apply_behaviours (ClutterScript *script, apply_behaviours (ClutterScript *script,
ClutterActor *actor, ClutterActor *actor,
GList *behaviours) ObjectInfo *oinfo)
{ {
GObject *object; 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; const gchar *name = l->data;
@ -949,7 +968,11 @@ apply_behaviours (ClutterScript *script,
oinfo = g_hash_table_lookup (script->priv->objects, name); oinfo = g_hash_table_lookup (script->priv->objects, name);
if (oinfo) if (oinfo)
object = clutter_script_construct_object (script, oinfo); object = clutter_script_construct_object (script, oinfo);
else }
if (!object)
{
unresolved = g_list_prepend (unresolved, g_strdup (name));
continue; continue;
} }
@ -959,17 +982,23 @@ apply_behaviours (ClutterScript *script,
clutter_behaviour_apply (CLUTTER_BEHAVIOUR (object), actor); 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 static void
add_children (ClutterScript *script, add_children (ClutterScript *script,
ClutterContainer *container, ClutterContainer *container,
GList *children) ObjectInfo *oinfo)
{ {
GObject *object; 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; const gchar *name = l->data;
@ -981,7 +1010,11 @@ add_children (ClutterScript *script,
oinfo = g_hash_table_lookup (script->priv->objects, name); oinfo = g_hash_table_lookup (script->priv->objects, name);
if (oinfo) if (oinfo)
object = clutter_script_construct_object (script, oinfo); object = clutter_script_construct_object (script, oinfo);
else }
if (!object)
{
unresolved = g_list_prepend (unresolved, g_strdup (name));
continue; continue;
} }
@ -991,6 +1024,11 @@ add_children (ClutterScript *script,
clutter_container_add_actor (container, CLUTTER_ACTOR (object)); 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 * static GObject *
@ -1000,9 +1038,11 @@ construct_stage (ClutterScript *script,
GObjectClass *oclass = g_type_class_ref (CLUTTER_TYPE_STAGE); GObjectClass *oclass = g_type_class_ref (CLUTTER_TYPE_STAGE);
GList *l; GList *l;
if (oinfo->object) if (oinfo->object && !oinfo->has_unresolved)
return oinfo->object; return oinfo->object;
if (!oinfo->object)
{
oinfo->object = G_OBJECT (clutter_stage_get_default ()); oinfo->object = G_OBJECT (clutter_stage_get_default ());
for (l = oinfo->properties; l; l = l->next) for (l = oinfo->properties; l; l = l->next)
@ -1032,20 +1072,18 @@ construct_stage (ClutterScript *script,
} }
g_object_set_property (oinfo->object, name, &value); g_object_set_property (oinfo->object, name, &value);
g_value_unset (&value); g_value_unset (&value);
} }
g_type_class_unref (oclass); g_type_class_unref (oclass);
if (oinfo->children)
{
/* we know ClutterStage is a ClutterContainer */
add_children (script,
CLUTTER_CONTAINER (oinfo->object),
oinfo->children);
} }
/* we know ClutterStage is a ClutterContainer */
if (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_object_set_data_full (oinfo->object, "clutter-script-name",
g_strdup (oinfo->id), g_strdup (oinfo->id),
g_free); g_free);
@ -1060,7 +1098,7 @@ clutter_script_construct_object (ClutterScript *script,
guint n_params, i; guint n_params, i;
GParameter *params; GParameter *params;
if (oinfo->object) if (oinfo->object && !oinfo->has_unresolved)
return oinfo->object; return oinfo->object;
if (oinfo->gtype == G_TYPE_INVALID) if (oinfo->gtype == G_TYPE_INVALID)
@ -1081,6 +1119,8 @@ clutter_script_construct_object (ClutterScript *script,
if (g_type_is_a (oinfo->gtype, CLUTTER_TYPE_STAGE)) if (g_type_is_a (oinfo->gtype, CLUTTER_TYPE_STAGE))
return construct_stage (script, oinfo); return construct_stage (script, oinfo);
if (!oinfo->object)
{
params = NULL; params = NULL;
translate_properties (script, oinfo, &n_params, &params); translate_properties (script, oinfo, &n_params, &params);
@ -1098,12 +1138,6 @@ clutter_script_construct_object (ClutterScript *script,
g_free (params); g_free (params);
if (CLUTTER_IS_CONTAINER (oinfo->object) && oinfo->children)
add_children (script, CLUTTER_CONTAINER (oinfo->object), oinfo->children);
if (CLUTTER_IS_ACTOR (oinfo->object) && oinfo->behaviours)
apply_behaviours (script, CLUTTER_ACTOR (oinfo->object), oinfo->behaviours);
if (CLUTTER_IS_BEHAVIOUR (oinfo->object) || if (CLUTTER_IS_BEHAVIOUR (oinfo->object) ||
CLUTTER_IS_TIMELINE (oinfo->object)) CLUTTER_IS_TIMELINE (oinfo->object))
oinfo->is_toplevel = TRUE; oinfo->is_toplevel = TRUE;
@ -1112,12 +1146,21 @@ clutter_script_construct_object (ClutterScript *script,
g_object_set_data_full (oinfo->object, "clutter-script-name", g_object_set_data_full (oinfo->object, "clutter-script-name",
g_strdup (oinfo->id), g_strdup (oinfo->id),
g_free); g_free);
}
if (CLUTTER_IS_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);
oinfo->has_unresolved = (oinfo->children || oinfo->behaviours);
return oinfo->object; return oinfo->object;
} }
static void static void
for_each_object (gpointer key, construct_each_object (gpointer key,
gpointer value, gpointer value,
gpointer data) gpointer data)
{ {
@ -1134,7 +1177,7 @@ json_parse_end (JsonParser *parser,
ClutterScript *script = user_data; ClutterScript *script = user_data;
ClutterScriptPrivate *priv = script->priv; 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 static void
@ -1166,7 +1209,16 @@ object_info_free (gpointer data)
g_list_free (oinfo->behaviours); g_list_free (oinfo->behaviours);
if (oinfo->is_toplevel && oinfo->object) 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); g_slice_free (ObjectInfo, oinfo);
} }
@ -1247,7 +1299,8 @@ clutter_script_new (void)
* the currently loaded ones, if any. * the currently loaded ones, if any.
* *
* Return value: on error, zero is returned and @error is set * 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 * Since: 0.6
*/ */
@ -1267,16 +1320,16 @@ clutter_script_load_from_file (ClutterScript *script,
g_free (priv->filename); g_free (priv->filename);
priv->filename = g_strdup (filename); priv->filename = g_strdup (filename);
priv->is_filename = TRUE; priv->is_filename = TRUE;
priv->last_merge_id += 1;
internal_error = NULL; internal_error = NULL;
json_parser_load_from_file (priv->parser, filename, &internal_error); json_parser_load_from_file (priv->parser, filename, &internal_error);
if (internal_error) if (internal_error)
{ {
g_propagate_error (error, internal_error); g_propagate_error (error, internal_error);
priv->last_merge_id -= 1;
return 0; return 0;
} }
else
priv->last_merge_id += 1;
return priv->last_merge_id; return priv->last_merge_id;
} }
@ -1293,7 +1346,8 @@ clutter_script_load_from_file (ClutterScript *script,
* the currently loaded ones, if any. * the currently loaded ones, if any.
* *
* Return value: on error, zero is returned and @error is set * 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 * Since: 0.6
*/ */
@ -1314,16 +1368,16 @@ clutter_script_load_from_data (ClutterScript *script,
g_free (priv->filename); g_free (priv->filename);
priv->filename = NULL; priv->filename = NULL;
priv->is_filename = FALSE; priv->is_filename = FALSE;
priv->last_merge_id += 1;
internal_error = NULL; internal_error = NULL;
json_parser_load_from_data (priv->parser, data, length, &internal_error); json_parser_load_from_data (priv->parser, data, length, &internal_error);
if (internal_error) if (internal_error)
{ {
g_propagate_error (error, internal_error); g_propagate_error (error, internal_error);
priv->last_merge_id -= 1;
return 0; return 0;
} }
else
priv->last_merge_id += 1;
return priv->last_merge_id; return priv->last_merge_id;
} }
@ -1409,6 +1463,84 @@ clutter_script_get_objects (ClutterScript *script,
return retval; 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 gboolean
clutter_script_enum_from_string (GType type, clutter_script_enum_from_string (GType type,
const gchar *string, const gchar *string,
@ -1538,16 +1670,6 @@ clutter_script_flags_from_string (GType type,
return ret; return ret;
} }
gboolean
clutter_script_value_from_data (ClutterScript *script,
GType gtype,
const gchar *data,
GValue *value,
GError **error)
{
return FALSE;
}
GQuark GQuark
clutter_script_error_quark (void) clutter_script_error_quark (void)
{ {

View File

@ -97,6 +97,9 @@ GObject * clutter_script_get_object (ClutterScript *script,
GList * clutter_script_get_objects (ClutterScript *script, GList * clutter_script_get_objects (ClutterScript *script,
const gchar *first_name, const gchar *first_name,
...) G_GNUC_NULL_TERMINATED; ...) G_GNUC_NULL_TERMINATED;
void clutter_script_unmerge_objects (ClutterScript *script,
guint merge_id);
void clutter_script_ensure_objects (ClutterScript *script);
G_END_DECLS 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> 2007-10-10 Emmanuele Bassi <ebassi@openedhand.com>
* clutter-sections.txt: Add new API and rearrange the subsections. * 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_load_from_file
clutter_script_get_object clutter_script_get_object
clutter_script_get_objects clutter_script_get_objects
clutter_script_unmerge_objects
clutter_script_ensure_objects
<SUBSECTION Standard> <SUBSECTION Standard>
CLUTTER_TYPE_SCRIPT CLUTTER_TYPE_SCRIPT
CLUTTER_SCRIPT CLUTTER_SCRIPT

View File

@ -5,6 +5,22 @@
#include <clutter/clutter.h> #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 = 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 int
main (int argc, char *argv[]) main (int argc, char *argv[])
{ {
ClutterScript *script; GObject *stage, *timeline, *blue_button;
GObject *stage, *timeline;
GError *error = NULL; GError *error = NULL;
clutter_init (&argc, &argv); clutter_init (&argc, &argv);
@ -69,9 +92,25 @@ main (int argc, char *argv[])
return EXIT_FAILURE; 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"); stage = clutter_script_get_object (script, "main-stage");
clutter_actor_show (CLUTTER_ACTOR (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"); timeline = clutter_script_get_object (script, "main-timeline");
clutter_timeline_start (CLUTTER_TIMELINE (timeline)); clutter_timeline_start (CLUTTER_TIMELINE (timeline));

View File

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