[clutter] script: Allow connecting signal to ClutterState states
- From: Emmanuele Bassi <ebassi src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [clutter] script: Allow connecting signal to ClutterState states
- Date: Mon, 13 Jun 2011 12:47:30 +0000 (UTC)
commit dd8cf63a62018704b59a84f76eb73e9568257ebb
Author: Emmanuele Bassi <ebassi linux intel com>
Date: Mon Feb 7 13:48:58 2011 +0000
script: Allow connecting signal to ClutterState states
One of the uses of a ClutterState state machine along with ClutterScript
is to provide a quick way to transition from state to state in response
to signal emitted on specific instances.
Connecting a real function, in code, to a specific signal does not
improve the ease of use of ClutterScript to define scenes.
By adding a new signal definition to the current one we can have both a
simple way to define application logic in code and in the UI definition
file.
The new syntax is trivial:
{
"name" : <signal name>,
"state" : <state machine script id>,
"target-state" : <target state>
}
The ClutterState instance is identified by its script id, and the target
state is resolved at run-time, so it can be defined both in
ClutterScript or in code. Ideally, we should find a way to associate a
default ClutterState instance to the ClutterScript one that parses the
definition; this way we would be able to remove the "state" member, or
even "style" the behaviour of an object by replacing the ClutterState
instance.
The implementation uses a signal emission hook, to avoid knowing the
signal signature; we check the emitter of the signal against the object
that defined the signal, to avoid erroneous state changes.
clutter/clutter-script-parser.c | 116 +++++++++++++++++++++++++-------------
clutter/clutter-script-private.h | 4 +
clutter/clutter-script.c | 113 ++++++++++++++++++++++++++++++++----
3 files changed, 181 insertions(+), 52 deletions(-)
---
diff --git a/clutter/clutter-script-parser.c b/clutter/clutter-script-parser.c
index aa786fc..27154f2 100644
--- a/clutter/clutter-script-parser.c
+++ b/clutter/clutter-script-parser.c
@@ -560,12 +560,9 @@ parse_signals (ClutterScript *script,
for (i = 0; i < array_len; i++)
{
JsonNode *val = json_array_get_element (array, i);
+ SignalInfo *sinfo = NULL;
JsonObject *object;
- SignalInfo *sinfo;
const gchar *name;
- const gchar *handler;
- const gchar *connect;
- GConnectFlags flags = 0;
if (JSON_NODE_TYPE (val) != JSON_NODE_OBJECT)
{
@@ -595,56 +592,97 @@ parse_signals (ClutterScript *script,
}
}
- /* mandatory: "handler" */
- if (!json_object_has_member (object, "handler"))
+ /* mandatory: "state" */
+ if (json_object_has_member (object, "state"))
{
- _clutter_script_warn_missing_attribute (script, NULL, "handler");
- continue;
+ const gchar *state;
+ const gchar *target;
+
+ state = json_object_get_string_member (object, "state");
+ if (state == NULL)
+ {
+ _clutter_script_warn_invalid_value (script,
+ "state", "string",
+ val);
+ continue;
+ }
+
+ target = json_object_get_string_member (object, "target-state");
+ if (target == NULL)
+ {
+ _clutter_script_warn_invalid_value (script,
+ "target-state", "string",
+ val);
+ continue;
+ }
+
+ CLUTTER_NOTE (SCRIPT,
+ "Added signal '%s' (state:%s, target:%s)",
+ name,
+ state, target);
+
+ sinfo = g_slice_new0 (SignalInfo);
+ sinfo->is_handler = FALSE;
+ sinfo->name = g_strdup (name);
+ sinfo->state = g_strdup (state);
+ sinfo->target = g_strdup (target);
}
- else
+
+ /* mandatory: "handler" */
+ if (json_object_has_member (object, "handler"))
{
+ const gchar *handler;
+ const gchar *connect;
+ GConnectFlags flags = 0;
+
handler = json_object_get_string_member (object, "handler");
- if (!handler)
+ if (handler == NULL)
{
_clutter_script_warn_invalid_value (script,
"handler", "string",
val);
continue;
}
- }
-
- /* optional: "object" */
- if (json_object_has_member (object, "object"))
- connect = json_object_get_string_member (object, "object");
- else
- connect = NULL;
- /* optional: "after" */
- if (json_object_has_member (object, "after"))
- {
- if (json_object_get_boolean_member (object, "after"))
- flags |= G_CONNECT_AFTER;
- }
+ /* optional: "object" */
+ if (json_object_has_member (object, "object"))
+ connect = json_object_get_string_member (object, "object");
+ else
+ connect = NULL;
- /* optional: "swapped" */
- if (json_object_has_member (object, "swapped"))
- {
- if (json_object_get_boolean_member (object, "swapped"))
- flags |= G_CONNECT_SWAPPED;
- }
+ /* optional: "after" */
+ if (json_object_has_member (object, "after"))
+ {
+ if (json_object_get_boolean_member (object, "after"))
+ flags |= G_CONNECT_AFTER;
+ }
- CLUTTER_NOTE (SCRIPT,
- "Parsing signal '%s' (handler:%s, object:%s, flags:%d)",
- name,
- handler, connect, flags);
+ /* optional: "swapped" */
+ if (json_object_has_member (object, "swapped"))
+ {
+ if (json_object_get_boolean_member (object, "swapped"))
+ flags |= G_CONNECT_SWAPPED;
+ }
- sinfo = g_slice_new0 (SignalInfo);
- sinfo->name = g_strdup (name);
- sinfo->handler = g_strdup (handler);
- sinfo->object = g_strdup (connect);
- sinfo->flags = flags;
+ CLUTTER_NOTE (SCRIPT,
+ "Added signal '%s' (handler:%s, object:%s, flags:%d)",
+ name,
+ handler, connect, flags);
+
+ sinfo = g_slice_new0 (SignalInfo);
+ sinfo->is_handler = TRUE;
+ sinfo->name = g_strdup (name);
+ sinfo->handler = g_strdup (handler);
+ sinfo->object = g_strdup (connect);
+ sinfo->flags = flags;
+ }
- retval = g_list_prepend (retval, sinfo);
+ if (sinfo != NULL)
+ retval = g_list_prepend (retval, sinfo);
+ else
+ _clutter_script_warn_missing_attribute (script,
+ NULL,
+ "handler or state");
}
return retval;
diff --git a/clutter/clutter-script-private.h b/clutter/clutter-script-private.h
index 87249bc..634e306 100644
--- a/clutter/clutter-script-private.h
+++ b/clutter/clutter-script-private.h
@@ -88,8 +88,12 @@ typedef struct {
gchar *name;
gchar *handler;
gchar *object;
+ gchar *state;
+ gchar *target;
GConnectFlags flags;
+
+ guint is_handler : 1;
} SignalInfo;
void property_info_free (gpointer data);
diff --git a/clutter/clutter-script.c b/clutter/clutter-script.c
index 23e5640..0143276 100644
--- a/clutter/clutter-script.c
+++ b/clutter/clutter-script.c
@@ -196,6 +196,7 @@
#include "clutter-behaviour.h"
#include "clutter-container.h"
#include "clutter-stage.h"
+#include "clutter-state.h"
#include "clutter-texture.h"
#include "clutter-script.h"
@@ -907,12 +908,47 @@ clutter_script_connect_signals (ClutterScript *script,
}
typedef struct {
+ ClutterState *state;
+ GObject *emitter;
+ gchar *target;
+} HookData;
+
+typedef struct {
ClutterScript *script;
ClutterScriptConnectFunc func;
gpointer user_data;
} SignalConnectData;
static void
+hook_data_free (gpointer data)
+{
+ if (G_LIKELY (data != NULL))
+ {
+ HookData *hook_data = data;
+
+ g_free (hook_data->target);
+ g_slice_free (HookData, hook_data);
+ }
+}
+
+static gboolean
+clutter_script_state_change_hook (GSignalInvocationHint *ihint,
+ guint n_params,
+ const GValue *params,
+ gpointer user_data)
+{
+ HookData *hook_data = user_data;
+ GObject *emitter;
+
+ emitter = g_value_get_object (¶ms[0]);
+
+ if (emitter == hook_data->emitter)
+ clutter_state_set_state (hook_data->state, hook_data->target);
+
+ return TRUE;
+}
+
+static void
connect_each_object (gpointer key,
gpointer value,
gpointer data)
@@ -929,24 +965,75 @@ connect_each_object (gpointer key,
for (l = oinfo->signals; l != NULL; l = l->next)
{
SignalInfo *sinfo = l->data;
- GObject *connect_object = NULL;
- if (sinfo->object)
- connect_object = clutter_script_get_object (script, sinfo->object);
+ if (sinfo->is_handler)
+ {
+ GObject *connect_object = NULL;
+
+ if (sinfo->object)
+ connect_object = clutter_script_get_object (script, sinfo->object);
- if (sinfo->object && !connect_object)
- unresolved = g_list_prepend (unresolved, sinfo);
+ 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);
+ }
+ }
else
{
- connect_data->func (script, object,
- sinfo->name,
- sinfo->handler,
- connect_object,
- sinfo->flags,
- connect_data->user_data);
-
- signal_info_free (sinfo);
+ GObject *state_object;
+ const gchar *signal_name, *signal_detail;
+ gchar **components;
+ GQuark signal_quark;
+ guint signal_id;
+ HookData *hook_data;
+
+ state_object = clutter_script_get_object (script, sinfo->state);
+ if (state_object == NULL)
+ continue;
+
+ components = g_strsplit (sinfo->name, "::", 2);
+ if (g_strv_length (components) == 2)
+ {
+ signal_name = components[0];
+ signal_detail = components[1];
+ }
+ else
+ {
+ signal_name = components[0];
+ signal_detail = NULL;
+ }
+
+ signal_id = g_signal_lookup (signal_name, G_OBJECT_TYPE (object));
+ if (signal_id == 0)
+ {
+ g_strfreev (components);
+ continue;
+ }
+
+ if (signal_detail != NULL)
+ signal_quark = g_quark_from_string (signal_detail);
+ else
+ signal_quark = 0;
+
+ hook_data = g_slice_new (HookData);
+ hook_data->emitter = object;
+ hook_data->state = CLUTTER_STATE (state_object);
+ hook_data->target = g_strdup (sinfo->target);
+
+ g_signal_add_emission_hook (signal_id, signal_quark,
+ clutter_script_state_change_hook,
+ hook_data,
+ hook_data_free);
}
+
+ signal_info_free (sinfo);
}
/* keep the unresolved signal handlers around, in case
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]