mirror of
https://github.com/brl/mutter.git
synced 2024-12-23 03:22:04 +00:00
2007-11-15 Emmanuele Bassi <ebassi@openedhand.com>
* clutter.symbols: Update with the new public symbols * clutter/clutter-script.h: * clutter/clutter-script-private.h: * clutter/clutter-script.c: (parse_signals), (json_object_end), (signal_info_free), (object_info_free): Parse the "signals" member for GObjects. (clutter_script_connect_signals), (clutter_script_connect_signals_full): Add new API for autoconnecting signal handlers using the UI definition files. * tests/test-script.c: * tests/test-script.json: Test signal autoconnection.
This commit is contained in:
parent
885adf33e6
commit
960619b9e3
18
ChangeLog
18
ChangeLog
@ -1,3 +1,21 @@
|
||||
2007-11-15 Emmanuele Bassi <ebassi@openedhand.com>
|
||||
|
||||
* clutter.symbols: Update with the new public symbols
|
||||
|
||||
* clutter/clutter-script.h:
|
||||
* clutter/clutter-script-private.h:
|
||||
* clutter/clutter-script.c:
|
||||
(parse_signals), (json_object_end),
|
||||
(signal_info_free), (object_info_free): Parse the "signals"
|
||||
member for GObjects.
|
||||
|
||||
(clutter_script_connect_signals),
|
||||
(clutter_script_connect_signals_full): Add new API for autoconnecting
|
||||
signal handlers using the UI definition files.
|
||||
|
||||
* tests/test-script.c:
|
||||
* tests/test-script.json: Test signal autoconnection.
|
||||
|
||||
2007-11-15 Matthew Allum <mallum@openedhand.com>
|
||||
|
||||
* clutter/Makefile.am:
|
||||
|
@ -23,7 +23,9 @@ clutter_actor_get_height
|
||||
clutter_actor_set_width
|
||||
clutter_actor_set_height
|
||||
clutter_actor_get_x
|
||||
clutter_actor_set_x
|
||||
clutter_actor_get_y
|
||||
clutter_actor_set_y
|
||||
clutter_actor_rotate_x
|
||||
clutter_actor_rotate_y
|
||||
clutter_actor_rotate_z
|
||||
@ -44,6 +46,7 @@ clutter_actor_get_id
|
||||
clutter_actor_set_clip
|
||||
clutter_actor_remove_clip
|
||||
clutter_actor_has_clip
|
||||
clutter_actor_get_clip
|
||||
clutter_actor_set_parent
|
||||
clutter_actor_get_parent
|
||||
clutter_actor_reparent
|
||||
@ -66,8 +69,7 @@ clutter_actor_move_by
|
||||
clutter_actor_get_vertices
|
||||
clutter_actor_apply_transform_to_point
|
||||
clutter_actor_set_reactive
|
||||
clutter_actor_unset_reactive
|
||||
clutter_actor_is_reactive
|
||||
clutter_actor_get_reactive
|
||||
clutter_get_actor_by_id
|
||||
clutter_alpha_get_type
|
||||
clutter_alpha_new
|
||||
@ -512,6 +514,8 @@ clutter_script_get_object
|
||||
clutter_script_get_objects
|
||||
clutter_script_unmerge_objects
|
||||
clutter_script_ensure_objects
|
||||
clutter_script_connect_signals
|
||||
clutter_script_connect_signals_full
|
||||
clutter_script_get_type_from_name
|
||||
clutter_get_script_id
|
||||
clutter_scriptable_get_type
|
||||
|
@ -43,6 +43,7 @@ typedef struct {
|
||||
GList *properties;
|
||||
GList *children;
|
||||
GList *behaviours;
|
||||
GList *signals;
|
||||
|
||||
GType gtype;
|
||||
GObject *object;
|
||||
@ -62,6 +63,14 @@ typedef struct {
|
||||
GParamSpec *pspec;
|
||||
} PropertyInfo;
|
||||
|
||||
typedef struct {
|
||||
gchar *name;
|
||||
gchar *handler;
|
||||
gchar *object;
|
||||
|
||||
GConnectFlags flags;
|
||||
} SignalInfo;
|
||||
|
||||
void property_info_free (gpointer data);
|
||||
|
||||
gboolean clutter_script_parse_node (ClutterScript *script,
|
||||
|
@ -111,6 +111,28 @@
|
||||
* however, be extracted using the #ClutterBehaviour and #ClutterAlpha
|
||||
* API respectively).
|
||||
*
|
||||
* Signal handlers can be defined inside a Clutter UI definition file and
|
||||
* then autoconnected to their respective signals using the
|
||||
* clutter_script_connect_signals() function:
|
||||
*
|
||||
* <programlisting>
|
||||
* ...
|
||||
* "signals" : [
|
||||
* { "name" : "button-press-event", "handler" : "on_button_press" },
|
||||
* {
|
||||
* "name" : "foo-signal",
|
||||
* "handler" : "after_foo",
|
||||
* "after" : true
|
||||
* },
|
||||
* ]
|
||||
* </programlisting>
|
||||
*
|
||||
* Signal handler definitions must have a "name" and a "handler" members;
|
||||
* they can also have the "after" and "swapped" boolean members (for the
|
||||
* signal connection flags %G_CONNECT_AFTER and %G_CONNECT_SWAPPED
|
||||
* respectively) and the "object" string member for calling
|
||||
* g_signal_connect_object() instead of g_signal_connect().
|
||||
*
|
||||
* Clutter reserves the following names, so classes defining properties
|
||||
* through the usual GObject registration process should avoid using these
|
||||
* names to avoid collisions:
|
||||
@ -121,6 +143,7 @@
|
||||
* "type_func" := the GType function name, for non-standard classes
|
||||
* "children" := an array of names or objects to add as children
|
||||
* "behaviours" := an array of names or objects to apply to an actor
|
||||
* "signals" := an array of signal definitions to connect to an object
|
||||
* ]]></programlisting>
|
||||
*
|
||||
* #ClutterScript is available since Clutter 0.6
|
||||
@ -285,6 +308,125 @@ parse_children (JsonNode *node)
|
||||
return g_list_reverse (retval);
|
||||
}
|
||||
|
||||
static GList *
|
||||
parse_signals (JsonNode *node)
|
||||
{
|
||||
JsonArray *array;
|
||||
GList *retval;
|
||||
guint array_len, i;
|
||||
|
||||
if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY)
|
||||
{
|
||||
g_warning ("Expecting an array for signal definitions");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
retval = NULL;
|
||||
array = json_node_get_array (node);
|
||||
array_len = json_array_get_length (array);
|
||||
|
||||
for (i = 0; i < array_len; i++)
|
||||
{
|
||||
JsonNode *val = json_array_get_element (array, i);
|
||||
JsonObject *object;
|
||||
SignalInfo *sinfo;
|
||||
const gchar *name;
|
||||
const gchar *handler;
|
||||
const gchar *connect;
|
||||
GConnectFlags flags = 0;
|
||||
|
||||
if (JSON_NODE_TYPE (val) != JSON_NODE_OBJECT)
|
||||
{
|
||||
g_warning ("Expecting an array of objects");
|
||||
continue;
|
||||
}
|
||||
|
||||
object = json_node_get_object (val);
|
||||
|
||||
/* mandatory: "name" */
|
||||
if (!json_object_has_member (object, "name"))
|
||||
{
|
||||
g_warning ("Missing `name' attribute in signal definition");
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
val = json_object_get_member (object, "name");
|
||||
if ((JSON_NODE_TYPE (val) == JSON_NODE_VALUE) &&
|
||||
json_node_get_string (val) != NULL)
|
||||
name = json_node_get_string (val);
|
||||
else
|
||||
{
|
||||
g_warning ("Signal `name' member must be a string");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* mandatory: "handler" */
|
||||
if (!json_object_has_member (object, "handler"))
|
||||
{
|
||||
g_warning ("Missing `handler' attribute in signal definition");
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
val = json_object_get_member (object, "handler");
|
||||
if ((JSON_NODE_TYPE (val) == JSON_NODE_VALUE) &&
|
||||
json_node_get_string (val) != NULL)
|
||||
handler = json_node_get_string (val);
|
||||
else
|
||||
{
|
||||
g_warning ("Signal `handler' member must be a string");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* optional: "object" */
|
||||
if (json_object_has_member (object, "object"))
|
||||
{
|
||||
val = json_object_get_member (object, "object");
|
||||
if ((JSON_NODE_TYPE (val) == JSON_NODE_VALUE) &&
|
||||
json_node_get_string (val) != NULL)
|
||||
connect = json_node_get_string (val);
|
||||
else
|
||||
connect = NULL;
|
||||
}
|
||||
else
|
||||
connect = NULL;
|
||||
|
||||
/* optional: "after" */
|
||||
if (json_object_has_member (object, "after"))
|
||||
{
|
||||
val = json_object_get_member (object, "after");
|
||||
if (json_node_get_boolean (val))
|
||||
flags |= G_CONNECT_AFTER;
|
||||
}
|
||||
|
||||
/* optional: "swapped" */
|
||||
if (json_object_has_member (object, "swapped"))
|
||||
{
|
||||
val = json_object_get_member (object, "swapped");
|
||||
if (json_node_get_boolean (val))
|
||||
flags |= G_CONNECT_SWAPPED;
|
||||
}
|
||||
|
||||
CLUTTER_NOTE (SCRIPT,
|
||||
"Parsing signal `%s' (handler:%s, object:%s, flags:%d)",
|
||||
name,
|
||||
handler, connect, flags);
|
||||
|
||||
sinfo = g_slice_new0 (SignalInfo);
|
||||
sinfo->name = g_strdup (name);
|
||||
sinfo->handler = g_strdup (handler);
|
||||
sinfo->object = g_strdup (connect);
|
||||
sinfo->flags = flags;
|
||||
|
||||
retval = g_list_prepend (retval, sinfo);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static GList *
|
||||
parse_behaviours (JsonNode *node)
|
||||
{
|
||||
@ -516,6 +658,14 @@ json_object_end (JsonParser *parser,
|
||||
json_object_remove_member (object, "behaviours");
|
||||
}
|
||||
|
||||
if (json_object_has_member (object, "signals"))
|
||||
{
|
||||
val = json_object_get_member (object, "signals");
|
||||
oinfo->signals = parse_signals (val);
|
||||
|
||||
json_object_remove_member (object, "signals");
|
||||
}
|
||||
|
||||
oinfo->is_toplevel = FALSE;
|
||||
oinfo->is_unmerged = FALSE;
|
||||
oinfo->has_unresolved = TRUE;
|
||||
@ -544,11 +694,13 @@ json_object_end (JsonParser *parser,
|
||||
|
||||
g_list_free (members);
|
||||
|
||||
CLUTTER_NOTE (SCRIPT, "Added object `%s' (type:%s, id:%d) with %d properties",
|
||||
CLUTTER_NOTE (SCRIPT,
|
||||
"Added object `%s' (type:%s, id:%d, props:%d, signals:%d)",
|
||||
oinfo->id,
|
||||
oinfo->class_name,
|
||||
oinfo->merge_id,
|
||||
g_list_length (oinfo->properties));
|
||||
g_list_length (oinfo->properties),
|
||||
g_list_length (oinfo->signals));
|
||||
|
||||
g_hash_table_replace (priv->objects, g_strdup (oinfo->id), oinfo);
|
||||
|
||||
@ -1228,6 +1380,21 @@ property_info_free (gpointer data)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
signal_info_free (gpointer data)
|
||||
{
|
||||
if (G_LIKELY (data))
|
||||
{
|
||||
SignalInfo *sinfo = data;
|
||||
|
||||
g_free (sinfo->name);
|
||||
g_free (sinfo->handler);
|
||||
g_free (sinfo->object);
|
||||
|
||||
g_slice_free (SignalInfo, sinfo);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
object_info_free (gpointer data)
|
||||
{
|
||||
@ -1242,6 +1409,9 @@ object_info_free (gpointer data)
|
||||
g_list_foreach (oinfo->properties, (GFunc) property_info_free, NULL);
|
||||
g_list_free (oinfo->properties);
|
||||
|
||||
g_list_foreach (oinfo->signals, (GFunc) signal_info_free, NULL);
|
||||
g_list_free (oinfo->signals);
|
||||
|
||||
/* these are ids */
|
||||
g_list_foreach (oinfo->children, (GFunc) g_free, NULL);
|
||||
g_list_free (oinfo->children);
|
||||
@ -1711,6 +1881,171 @@ clutter_get_script_id (GObject *gobject)
|
||||
return g_object_get_data (gobject, "clutter-script-id");
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GModule *module;
|
||||
gpointer data;
|
||||
} ConnectData;
|
||||
|
||||
static void
|
||||
clutter_script_default_connect (ClutterScript *script,
|
||||
GObject *object,
|
||||
const gchar *signal_name,
|
||||
const gchar *signal_handler,
|
||||
GObject *connect_object,
|
||||
GConnectFlags flags,
|
||||
gpointer user_data)
|
||||
{
|
||||
ConnectData *cd = user_data;
|
||||
GCallback func;
|
||||
|
||||
if (!g_module_symbol (cd->module, signal_handler, (gpointer)&func))
|
||||
{
|
||||
g_warning ("Could not find signal handler '%s'", signal_handler);
|
||||
return;
|
||||
}
|
||||
|
||||
CLUTTER_NOTE (SCRIPT,
|
||||
"connecting `%s::%s to %s (a:%d,s:%d,o:%s)",
|
||||
(connect_object ? g_type_name (G_OBJECT_TYPE (connect_object))
|
||||
: g_type_name (G_OBJECT_TYPE (object))),
|
||||
signal_name,
|
||||
signal_handler,
|
||||
(flags & G_CONNECT_AFTER),
|
||||
(flags & G_CONNECT_SWAPPED),
|
||||
connect_object ? g_type_name (G_OBJECT_TYPE (connect_object))
|
||||
: "<none>");
|
||||
|
||||
if (connect_object)
|
||||
g_signal_connect_object (object, signal_name, func, connect_object, flags);
|
||||
else
|
||||
g_signal_connect_data (object, signal_name, func, cd->data, NULL, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_script_connect_signals:
|
||||
* @script: a #ClutterScript
|
||||
* @user_data: data to be passed to the signal handlers, or %NULL
|
||||
*
|
||||
* Connects all the signals defined into a UI definition file to their
|
||||
* handlers.
|
||||
*
|
||||
* This method is a simpler variation of clutter_script_connect_signals_full().
|
||||
* It uses #GModule's introspective features (by opening the module %NULL)
|
||||
* to look at the application's symbol table. From here it tries to match
|
||||
* the signal handler names given in the interface description with
|
||||
* symbols in the application and connects the signals.
|
||||
*
|
||||
* Note that this function will not work correctly if #GModule is not
|
||||
* supported on the platform.
|
||||
*
|
||||
* Since: 0.6
|
||||
*/
|
||||
void
|
||||
clutter_script_connect_signals (ClutterScript *script,
|
||||
gpointer user_data)
|
||||
{
|
||||
ConnectData *cd;
|
||||
|
||||
g_return_if_fail (CLUTTER_IS_SCRIPT (script));
|
||||
|
||||
if (!g_module_supported ())
|
||||
g_critical ("clutter_script_connect_signals() requires working GModule");
|
||||
|
||||
cd = g_new (ConnectData, 1);
|
||||
cd->module = g_module_open (NULL, G_MODULE_BIND_LAZY);
|
||||
cd->data = user_data;
|
||||
|
||||
clutter_script_connect_signals_full (script,
|
||||
clutter_script_default_connect,
|
||||
cd);
|
||||
|
||||
g_module_close (cd->module);
|
||||
|
||||
g_free (cd);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
ClutterScript *script;
|
||||
ClutterScriptConnectFunc func;
|
||||
gpointer user_data;
|
||||
} SignalConnectData;
|
||||
|
||||
static void
|
||||
connect_each_object (gpointer key,
|
||||
gpointer value,
|
||||
gpointer data)
|
||||
{
|
||||
SignalConnectData *connect_data = data;
|
||||
ClutterScript *script = connect_data->script;
|
||||
ObjectInfo *oinfo = value;
|
||||
GObject *object = oinfo->object;
|
||||
GList *unresolved, *l;
|
||||
|
||||
if (G_UNLIKELY (!oinfo->object))
|
||||
oinfo->object = clutter_script_construct_object (script, oinfo);
|
||||
|
||||
unresolved = NULL;
|
||||
for (l = oinfo->signals; l != NULL; l = l->next)
|
||||
{
|
||||
SignalInfo *sinfo = l->data;
|
||||
GObject *connect_object;
|
||||
|
||||
if (sinfo->object)
|
||||
connect_object = clutter_script_get_object (script, sinfo->object);
|
||||
else
|
||||
connect_object = NULL;
|
||||
|
||||
if (sinfo->object && !connect_object)
|
||||
unresolved = g_list_prepend (unresolved, sinfo);
|
||||
else
|
||||
{
|
||||
connect_data->func (script, object,
|
||||
sinfo->name,
|
||||
sinfo->handler,
|
||||
connect_object,
|
||||
sinfo->flags,
|
||||
connect_data->user_data);
|
||||
|
||||
signal_info_free (sinfo);
|
||||
}
|
||||
}
|
||||
|
||||
g_list_free (oinfo->signals);
|
||||
oinfo->signals = unresolved;
|
||||
}
|
||||
|
||||
/**
|
||||
* clutter_script_connect_signals_full:
|
||||
* @script: a #ClutterScript
|
||||
* @func: signal connection function
|
||||
* @user_data: data to be passed to the signal handlers, or %NULL
|
||||
*
|
||||
* Connects all the signals defined into a UI definition file to their
|
||||
* handlers.
|
||||
*
|
||||
* This function is similar to clutter_script_connect_signals() but it
|
||||
* does not require GModule to be supported. It is mainly targeted at
|
||||
* interpreted languages for controlling the signal connection.
|
||||
*
|
||||
* Since: 0.6
|
||||
*/
|
||||
void
|
||||
clutter_script_connect_signals_full (ClutterScript *script,
|
||||
ClutterScriptConnectFunc func,
|
||||
gpointer user_data)
|
||||
{
|
||||
SignalConnectData data;
|
||||
|
||||
g_return_if_fail (CLUTTER_IS_SCRIPT (script));
|
||||
g_return_if_fail (func != NULL);
|
||||
|
||||
data.script = script;
|
||||
data.func = func;
|
||||
data.user_data = user_data;
|
||||
|
||||
g_hash_table_foreach (script->priv->objects, connect_each_object, &data);
|
||||
}
|
||||
|
||||
GQuark
|
||||
clutter_script_error_quark (void)
|
||||
{
|
||||
|
@ -41,6 +41,31 @@ typedef struct _ClutterScript ClutterScript;
|
||||
typedef struct _ClutterScriptPrivate ClutterScriptPrivate;
|
||||
typedef struct _ClutterScriptClass ClutterScriptClass;
|
||||
|
||||
/**
|
||||
* ClutterScriptConnectFunc:
|
||||
* @script: a #ClutterScript
|
||||
* @object: the object to connect
|
||||
* @signal_name: the name of the signal
|
||||
* @handler_name: the name of the signal handler
|
||||
* @connect_object: the object to connect the signal to, or %NULL
|
||||
* @flags: signal connection flags
|
||||
* @user_data: user data to pass to the signal handler
|
||||
*
|
||||
* This is the signature of a function used to connect signals. It is used
|
||||
* by the clutter_script_connect_signals_full() function. It is mainly
|
||||
* intended for interpreted language bindings, but could be useful where the
|
||||
* programmer wants more control over the signal connection process.
|
||||
*
|
||||
* Since: 0.6
|
||||
*/
|
||||
typedef void (* ClutterScriptConnectFunc) (ClutterScript *script,
|
||||
GObject *object,
|
||||
const gchar *signal_name,
|
||||
const gchar *handler_name,
|
||||
GObject *connect_object,
|
||||
GConnectFlags flags,
|
||||
gpointer user_data);
|
||||
|
||||
/**
|
||||
* ClutterScriptError:
|
||||
* @CLUTTER_SCRIPT_ERROR_INVALID_VALUE: Invalid value
|
||||
@ -111,6 +136,12 @@ GType clutter_script_get_type_from_name (ClutterScript *script,
|
||||
|
||||
G_CONST_RETURN gchar *clutter_get_script_id (GObject *object);
|
||||
|
||||
void clutter_script_connect_signals (ClutterScript *script,
|
||||
gpointer user_data);
|
||||
void clutter_script_connect_signals_full (ClutterScript *script,
|
||||
ClutterScriptConnectFunc func,
|
||||
gpointer user_data);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __CLUTTER_SCRIPT_H__ */
|
||||
|
@ -1,3 +1,8 @@
|
||||
2007-11-15 Emmanuele Bassi <ebassi@openedhand.com>
|
||||
|
||||
* clutter-sections.txt: Add new ClutterScript signal connection
|
||||
functions.
|
||||
|
||||
2007-11-15 Neil J. Patel <njp@o-hand.com>
|
||||
|
||||
* clutter-sections.txt:
|
||||
|
@ -1096,12 +1096,22 @@ clutter_script_new
|
||||
ClutterScriptError
|
||||
clutter_script_load_from_data
|
||||
clutter_script_load_from_file
|
||||
|
||||
<SUBSECTION>
|
||||
clutter_script_get_object
|
||||
clutter_script_get_objects
|
||||
clutter_script_unmerge_objects
|
||||
clutter_script_ensure_objects
|
||||
|
||||
<SUBSECTION>
|
||||
ClutterScriptConnectFunc
|
||||
clutter_script_connect_signals
|
||||
clutter_script_connect_signals_full
|
||||
|
||||
<SUBSECTION>
|
||||
clutter_script_get_type_from_name
|
||||
clutter_get_script_id
|
||||
|
||||
<SUBSECTION Standard>
|
||||
CLUTTER_TYPE_SCRIPT
|
||||
CLUTTER_SCRIPT
|
||||
|
@ -136,6 +136,8 @@ main (int argc, char *argv[])
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
clutter_script_connect_signals (script, NULL);
|
||||
|
||||
res = clutter_script_get_objects (script,
|
||||
"main-stage", &stage,
|
||||
"red-button", &red_button,
|
||||
|
@ -5,6 +5,9 @@
|
||||
"color" : "white",
|
||||
"width" : 500,
|
||||
"height" : 400,
|
||||
"signals" : [
|
||||
{ "name" : "key-press-event", "handler" : "clutter_main_quit" }
|
||||
],
|
||||
"children" : [
|
||||
{
|
||||
"id" : "red-button",
|
||||
@ -27,6 +30,9 @@
|
||||
"height" : 100,
|
||||
"visible" : true,
|
||||
"reactive" : true
|
||||
"signals" : [
|
||||
{ "name" : "button-press-event", "handler" : "clutter_main_quit" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id" : "red-hand",
|
||||
@ -34,6 +40,8 @@
|
||||
"pixbuf" : "redhand.png",
|
||||
"x" : 50,
|
||||
"y" : 150,
|
||||
"width" : 100,
|
||||
"height" : 100,
|
||||
"opacity" : 100,
|
||||
"visible" : true,
|
||||
"behaviours" : [ "rotate-behaviour", "fade-behaviour" ]
|
||||
|
Loading…
Reference in New Issue
Block a user