[clutter] script: Allow connecting signal to ClutterState states



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 (&params[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]