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.
This commit is contained in:
parent
2c2bdc1d2c
commit
dd8cf63a62
@ -560,12 +560,9 @@ parse_signals (ClutterScript *script,
|
|||||||
for (i = 0; i < array_len; i++)
|
for (i = 0; i < array_len; i++)
|
||||||
{
|
{
|
||||||
JsonNode *val = json_array_get_element (array, i);
|
JsonNode *val = json_array_get_element (array, i);
|
||||||
|
SignalInfo *sinfo = NULL;
|
||||||
JsonObject *object;
|
JsonObject *object;
|
||||||
SignalInfo *sinfo;
|
|
||||||
const gchar *name;
|
const gchar *name;
|
||||||
const gchar *handler;
|
|
||||||
const gchar *connect;
|
|
||||||
GConnectFlags flags = 0;
|
|
||||||
|
|
||||||
if (JSON_NODE_TYPE (val) != JSON_NODE_OBJECT)
|
if (JSON_NODE_TYPE (val) != JSON_NODE_OBJECT)
|
||||||
{
|
{
|
||||||
@ -595,23 +592,57 @@ parse_signals (ClutterScript *script,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* mandatory: "handler" */
|
/* mandatory: "state" */
|
||||||
if (!json_object_has_member (object, "handler"))
|
if (json_object_has_member (object, "state"))
|
||||||
{
|
{
|
||||||
_clutter_script_warn_missing_attribute (script, NULL, "handler");
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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");
|
handler = json_object_get_string_member (object, "handler");
|
||||||
if (!handler)
|
if (handler == NULL)
|
||||||
{
|
{
|
||||||
_clutter_script_warn_invalid_value (script,
|
_clutter_script_warn_invalid_value (script,
|
||||||
"handler", "string",
|
"handler", "string",
|
||||||
val);
|
val);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* optional: "object" */
|
/* optional: "object" */
|
||||||
if (json_object_has_member (object, "object"))
|
if (json_object_has_member (object, "object"))
|
||||||
@ -634,17 +665,24 @@ parse_signals (ClutterScript *script,
|
|||||||
}
|
}
|
||||||
|
|
||||||
CLUTTER_NOTE (SCRIPT,
|
CLUTTER_NOTE (SCRIPT,
|
||||||
"Parsing signal '%s' (handler:%s, object:%s, flags:%d)",
|
"Added signal '%s' (handler:%s, object:%s, flags:%d)",
|
||||||
name,
|
name,
|
||||||
handler, connect, flags);
|
handler, connect, flags);
|
||||||
|
|
||||||
sinfo = g_slice_new0 (SignalInfo);
|
sinfo = g_slice_new0 (SignalInfo);
|
||||||
|
sinfo->is_handler = TRUE;
|
||||||
sinfo->name = g_strdup (name);
|
sinfo->name = g_strdup (name);
|
||||||
sinfo->handler = g_strdup (handler);
|
sinfo->handler = g_strdup (handler);
|
||||||
sinfo->object = g_strdup (connect);
|
sinfo->object = g_strdup (connect);
|
||||||
sinfo->flags = flags;
|
sinfo->flags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sinfo != NULL)
|
||||||
retval = g_list_prepend (retval, sinfo);
|
retval = g_list_prepend (retval, sinfo);
|
||||||
|
else
|
||||||
|
_clutter_script_warn_missing_attribute (script,
|
||||||
|
NULL,
|
||||||
|
"handler or state");
|
||||||
}
|
}
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
|
@ -88,8 +88,12 @@ typedef struct {
|
|||||||
gchar *name;
|
gchar *name;
|
||||||
gchar *handler;
|
gchar *handler;
|
||||||
gchar *object;
|
gchar *object;
|
||||||
|
gchar *state;
|
||||||
|
gchar *target;
|
||||||
|
|
||||||
GConnectFlags flags;
|
GConnectFlags flags;
|
||||||
|
|
||||||
|
guint is_handler : 1;
|
||||||
} SignalInfo;
|
} SignalInfo;
|
||||||
|
|
||||||
void property_info_free (gpointer data);
|
void property_info_free (gpointer data);
|
||||||
|
@ -196,6 +196,7 @@
|
|||||||
#include "clutter-behaviour.h"
|
#include "clutter-behaviour.h"
|
||||||
#include "clutter-container.h"
|
#include "clutter-container.h"
|
||||||
#include "clutter-stage.h"
|
#include "clutter-stage.h"
|
||||||
|
#include "clutter-state.h"
|
||||||
#include "clutter-texture.h"
|
#include "clutter-texture.h"
|
||||||
|
|
||||||
#include "clutter-script.h"
|
#include "clutter-script.h"
|
||||||
@ -906,12 +907,47 @@ clutter_script_connect_signals (ClutterScript *script,
|
|||||||
g_free (cd);
|
g_free (cd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ClutterState *state;
|
||||||
|
GObject *emitter;
|
||||||
|
gchar *target;
|
||||||
|
} HookData;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
ClutterScript *script;
|
ClutterScript *script;
|
||||||
ClutterScriptConnectFunc func;
|
ClutterScriptConnectFunc func;
|
||||||
gpointer user_data;
|
gpointer user_data;
|
||||||
} SignalConnectData;
|
} 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
|
static void
|
||||||
connect_each_object (gpointer key,
|
connect_each_object (gpointer key,
|
||||||
gpointer value,
|
gpointer value,
|
||||||
@ -929,6 +965,9 @@ connect_each_object (gpointer key,
|
|||||||
for (l = oinfo->signals; l != NULL; l = l->next)
|
for (l = oinfo->signals; l != NULL; l = l->next)
|
||||||
{
|
{
|
||||||
SignalInfo *sinfo = l->data;
|
SignalInfo *sinfo = l->data;
|
||||||
|
|
||||||
|
if (sinfo->is_handler)
|
||||||
|
{
|
||||||
GObject *connect_object = NULL;
|
GObject *connect_object = NULL;
|
||||||
|
|
||||||
if (sinfo->object)
|
if (sinfo->object)
|
||||||
@ -944,10 +983,58 @@ connect_each_object (gpointer key,
|
|||||||
connect_object,
|
connect_object,
|
||||||
sinfo->flags,
|
sinfo->flags,
|
||||||
connect_data->user_data);
|
connect_data->user_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
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);
|
signal_info_free (sinfo);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* keep the unresolved signal handlers around, in case
|
/* keep the unresolved signal handlers around, in case
|
||||||
* clutter_script_connect_signals() is called multiple
|
* clutter_script_connect_signals() is called multiple
|
||||||
|
Loading…
x
Reference in New Issue
Block a user