+2007-10-10 Matthew Allum <mallum@openedhand.com>
+
+ * clutter/clutter-actor.c:
+ * clutter/clutter-actor.h:
+ * clutter/clutter-event.c:
+ * clutter/clutter-main.c:
+ * tests/test-events.c:
+ Add basic W3 DOM event 'capture' like functionality.
+
2007-10-10 Emmanuele Bassi <ebassi@openedhand.com>
* clutter/clutter-script-private.h:
PARENT_SET,
EVENT,
+ EVENT_CAPTURED,
EVENT_AFTER,
BUTTON_PRESS_EVENT,
BUTTON_RELEASE_EVENT,
FOCUS_OUT,
ENTER_EVENT,
LEAVE_EVENT,
-
LAST_SIGNAL
};
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterActorClass, enter),
- NULL, NULL,
- clutter_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
+ _clutter_boolean_handled_accumulator, NULL,
+ clutter_marshal_BOOLEAN__BOXED,
+ G_TYPE_BOOLEAN, 1,
+ CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
/**
* ClutterActor::leave:
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterActorClass, leave),
- NULL, NULL,
- clutter_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
+ _clutter_boolean_handled_accumulator, NULL,
+ clutter_marshal_BOOLEAN__BOXED,
+ G_TYPE_BOOLEAN, 1,
+ CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
+ /**
+ * ClutterActor::captured-event:
+ * @actor: the actor which the pointer has left
+ *
+ * The ::leave signal is emitted when the pointer leaves the @actor.
+ *
+ * Since: 0.6
+ */
+ actor_signals[EVENT_CAPTURED] =
+ g_signal_new ("captured-event",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ClutterActorClass, captured),
+ _clutter_boolean_handled_accumulator, NULL,
+ clutter_marshal_BOOLEAN__BOXED,
+ G_TYPE_BOOLEAN, 1,
+ CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
klass->show = clutter_actor_real_show;
klass->show_all = clutter_actor_show;
* clutter_actor_event:
* @actor: a #ClutterActor
* @event: a #ClutterEvent
+ * @capture: TRUE if event in in capture phase, FALSE otherwise.
*
* This function is used to emit an event on the main stage.
* You should rarely need to use this function, except for
*/
gboolean
clutter_actor_event (ClutterActor *actor,
- ClutterEvent *event)
+ ClutterEvent *event,
+ gboolean capture)
{
gboolean retval = TRUE;
gint signal_num = -1;
g_return_val_if_fail (event != NULL, FALSE);
g_object_ref (actor);
+
+ if (capture)
+ {
+ g_signal_emit (actor, actor_signals[EVENT_CAPTURED], 0,
+ event, &retval);
+ goto out;
+ }
g_signal_emit (actor, actor_signals[EVENT], 0, event, &retval);
if (!retval)
{
switch (event->type)
- {
- case CLUTTER_NOTHING:
- break;
- case CLUTTER_BUTTON_PRESS:
- signal_num = BUTTON_PRESS_EVENT;
- break;
- case CLUTTER_BUTTON_RELEASE:
- signal_num = BUTTON_RELEASE_EVENT;
- break;
- case CLUTTER_SCROLL:
- signal_num = SCROLL_EVENT;
- break;
- case CLUTTER_KEY_PRESS:
- signal_num = KEY_PRESS_EVENT;
- break;
- case CLUTTER_KEY_RELEASE:
- signal_num = KEY_RELEASE_EVENT;
- break;
- case CLUTTER_MOTION:
- signal_num = MOTION_EVENT;
- break;
- case CLUTTER_ENTER:
- signal_num = ENTER_EVENT;
- break;
- case CLUTTER_LEAVE:
- signal_num = LEAVE_EVENT;
- break;
- case CLUTTER_DELETE:
- case CLUTTER_DESTROY_NOTIFY:
- case CLUTTER_CLIENT_MESSAGE:
- default:
- signal_num = -1;
- break;
- }
+ {
+ case CLUTTER_NOTHING:
+ break;
+ case CLUTTER_BUTTON_PRESS:
+ signal_num = BUTTON_PRESS_EVENT;
+ break;
+ case CLUTTER_BUTTON_RELEASE:
+ signal_num = BUTTON_RELEASE_EVENT;
+ break;
+ case CLUTTER_SCROLL:
+ signal_num = SCROLL_EVENT;
+ break;
+ case CLUTTER_KEY_PRESS:
+ signal_num = KEY_PRESS_EVENT;
+ break;
+ case CLUTTER_KEY_RELEASE:
+ signal_num = KEY_RELEASE_EVENT;
+ break;
+ case CLUTTER_MOTION:
+ signal_num = MOTION_EVENT;
+ break;
+ case CLUTTER_ENTER:
+ signal_num = ENTER_EVENT;
+ break;
+ case CLUTTER_LEAVE:
+ signal_num = LEAVE_EVENT;
+ break;
+ case CLUTTER_DELETE:
+ case CLUTTER_DESTROY_NOTIFY:
+ case CLUTTER_CLIENT_MESSAGE:
+ default:
+ signal_num = -1;
+ break;
+ }
+
if (signal_num != -1)
- g_signal_emit (actor, actor_signals[signal_num], 0, event, &retval);
+ g_signal_emit (actor, actor_signals[signal_num], 0,
+ event, &retval);
}
- g_signal_emit (actor, actor_signals[EVENT_AFTER], 0, event);
+
+ if (!retval)
+ g_signal_emit (actor, actor_signals[EVENT_AFTER], 0, event);
+
+ out:
g_object_unref (actor);
ClutterCrossingEvent *event);
void (* leave) (ClutterActor *actor,
ClutterCrossingEvent *event);
+ void (* captured) (ClutterActor *actor,
+ ClutterEvent *event);
void (* focus_in) (ClutterActor *actor);
void (* focus_out) (ClutterActor *actor);
/* Per actor event handling - may change */
gboolean
clutter_actor_event (ClutterActor *actor,
- ClutterEvent *event);
+ ClutterEvent *event,
+ gboolean capture);
void
clutter_actor_set_reactive (ClutterActor *actor);
if (G_LIKELY (event))
{
ClutterActor *source = NULL;
-
- source = clutter_event_get_source (event);
- if (source)
- g_object_unref (source);
+ if (event->type == CLUTTER_LEAVE || event->type == CLUTTER_ENTER)
+ g_object_unref (event->crossing.related);
g_slice_free (ClutterEvent, event);
}
}
}
}
+static void
+deliver_event (ClutterEvent *event, ClutterActor *source)
+{
+#define MAX_EVENT_DEPTH 512
+
+ static ClutterActor **event_tree = NULL;
+ static gboolean lock = FALSE;
+
+ ClutterActor *actor;
+ gint i = 0, n_tree_events = 0;
+
+ g_return_if_fail (source != NULL);
+ g_return_if_fail (lock == FALSE);
+
+ lock = TRUE; /* Guard against reentrancy */
+
+ /* Sorry Mr Bassi. */
+ if (event_tree == NULL)
+ event_tree = g_new0(ClutterActor*, MAX_EVENT_DEPTH);
+
+ actor = source;
+
+ /* Build 'tree' of events */
+ while (actor && n_tree_events < MAX_EVENT_DEPTH)
+ {
+ if (clutter_actor_is_reactive (actor) ||
+ clutter_actor_get_parent (actor) == NULL)
+ event_tree[n_tree_events++] = g_object_ref (actor);
+
+ actor = clutter_actor_get_parent (actor);
+ }
+
+ /* Capture */
+ for (i=n_tree_events-1; i >= 0; i--)
+ if (clutter_actor_event (event_tree[i], event, TRUE))
+ goto done;
+
+ /* Bubble */
+ for (i=0; i < n_tree_events; i++)
+ if (clutter_actor_event (event_tree[i], event, FALSE))
+ goto done;
+
+ done:
+
+ for (i=0; i < n_tree_events; i++)
+ g_object_unref (event_tree[i]);
+
+ lock = FALSE;
+}
+
/**
* clutter_do_event
* @event: a #ClutterEvent.
ClutterMainContext *context;
ClutterBackend *backend;
ClutterActor *stage;
+
static ClutterActor *motion_last_actor = NULL;
context = clutter_context_get_default ();
g_return_if_fail (actor != NULL);
- while (actor)
- {
- if (clutter_actor_is_reactive (actor) ||
- clutter_actor_get_parent (actor) == NULL /* STAGE */ )
- {
- CLUTTER_NOTE (EVENT, "forwarding event to reactive actor");
- if (clutter_actor_event (actor, event))
- break;
- }
-
- actor = clutter_actor_get_parent (actor);
- }
+ deliver_event (event, actor);
}
break;
case CLUTTER_DESTROY_NOTIFY:
g_return_if_fail (actor != NULL);
- event->key.source = g_object_ref (actor);
-
- /* bubble up */
- do
- {
- if (clutter_actor_event (actor, event))
- break;
-
- actor = clutter_actor_get_parent (actor);
- }
- while (actor != NULL);
+ event->key.source = actor;
+ deliver_event (event, actor);
}
break;
case CLUTTER_MOTION:
if (context->motion_events_per_actor == FALSE)
{
/* Only stage gets motion events */
- event->motion.source = g_object_ref (stage);
- clutter_actor_event (stage, event);
+ event->motion.source = stage;
+ clutter_actor_event (stage, event, FALSE);
break;
}
case CLUTTER_BUTTON_PRESS:
x, y,
CLUTTER_PICK_REACTIVE);
+ /* FIXME: for an optimisation should check if there are
+ * actually any reactive actors and avoid the pick all togeather
+ * (signalling just the stage). Should be big help for gles.
+ */
+
CLUTTER_NOTE (EVENT, "Reactive event received at %i, %i - actor: %p",
x, y, actor);
g_return_if_fail (actor != NULL);
if (event->type == CLUTTER_SCROLL)
- event->scroll.source = g_object_ref (actor);
+ event->scroll.source = actor;
else
- event->button.source = g_object_ref (actor);
+ event->button.source = actor;
/* Motion enter leave events */
if (event->type == CLUTTER_MOTION)
cev.crossing.flags = 0;
cev.crossing.x = x;
cev.crossing.y = y;
- cev.crossing.source = g_object_ref (motion_last_actor);
+ cev.crossing.source = motion_last_actor;
+ /* unref in free */
cev.crossing.related = g_object_ref (actor);
clutter_event_put (&cev); /* copys */
cev.crossing.flags = 0;
cev.crossing.x = x;
cev.crossing.y = y;
- cev.crossing.source = g_object_ref (actor);
+ cev.crossing.source = actor;
cev.crossing.related = g_object_ref (motion_last_actor);
clutter_event_put (&cev);
else
event_click_count_generate (event);
- /* Send the event to the actor and all parents always the
- * stage.
- *
- * FIXME: for an optimisation should check if there are
- * actually any reactive actors and avoid the pick all togeather
- * (signalling just the stage). Should be big help for gles.
- *
- * FIXME: Actors be able to stop emission.
- */
- while (actor)
- {
- if (clutter_actor_is_reactive (actor) ||
- clutter_actor_get_parent (actor) == NULL /* STAGE */ )
- {
- CLUTTER_NOTE (EVENT, "forwarding event to reactive actor");
- if (clutter_actor_event (actor, event))
- break;
- }
-
- actor = clutter_actor_get_parent (actor);
- }
+ deliver_event (event, actor);
}
break;
case CLUTTER_STAGE_STATE:
return FALSE;
/* emit raw event */
- if (clutter_actor_event (CLUTTER_ACTOR (stage), event))
+ if (clutter_actor_event (CLUTTER_ACTOR (stage), event, FALSE))
return TRUE;
if (event->stage_state.changed_mask & CLUTTER_STAGE_STATE_FULLSCREEN)
IsMotion = TRUE;
clutter_enable_motion_events (IsMotion);
+
+ return FALSE;
+}
+
+static gboolean
+capture_cb (ClutterActor *actor,
+ ClutterEvent *event,
+ gpointer data)
+{
+ printf("captured event for type '%s'\n", G_OBJECT_TYPE_NAME(actor));
+
+ return FALSE;
}
static void
stage = clutter_stage_get_default ();
g_signal_connect (stage, "event", G_CALLBACK (input_cb), "stage");
+
g_signal_connect (stage, "fullscreen",
G_CALLBACK (stage_state_cb), "fullscreen");
g_signal_connect (stage, "unfullscreen",
g_signal_connect (stage, "deactivate",
G_CALLBACK (stage_state_cb), "deactivate");
+ g_signal_connect (stage, "captured-event", G_CALLBACK (capture_cb), NULL);
+
focus_box = clutter_rectangle_new_with_color (&ncol);
clutter_container_add (CLUTTER_CONTAINER(stage), focus_box, NULL);
g_signal_connect (actor, "focus-in", G_CALLBACK (key_focus_in_cb),
focus_box);
+ g_signal_connect (actor, "captured-event", G_CALLBACK (capture_cb), NULL);
+
actor = clutter_rectangle_new_with_color (&bcol);
clutter_actor_set_size (actor, 100, 100);
clutter_actor_set_position (actor, 400, 100);