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.
This commit is contained in:
Matthew Allum 2007-10-10 13:04:34 +00:00
parent a4b7abd18e
commit 6216ce659c
7 changed files with 178 additions and 100 deletions

View File

@ -1,3 +1,12 @@
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:

View File

@ -106,6 +106,7 @@ enum
PARENT_SET,
EVENT,
EVENT_CAPTURED,
EVENT_AFTER,
BUTTON_PRESS_EVENT,
BUTTON_RELEASE_EVENT,
@ -117,7 +118,6 @@ enum
FOCUS_OUT,
ENTER_EVENT,
LEAVE_EVENT,
LAST_SIGNAL
};
@ -1469,9 +1469,10 @@ clutter_actor_class_init (ClutterActorClass *klass)
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:
@ -1486,10 +1487,28 @@ clutter_actor_class_init (ClutterActorClass *klass)
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;
@ -2977,6 +2996,7 @@ clutter_actor_lower_bottom (ClutterActor *self)
* 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
@ -2988,7 +3008,8 @@ clutter_actor_lower_bottom (ClutterActor *self)
*/
gboolean
clutter_actor_event (ClutterActor *actor,
ClutterEvent *event)
ClutterEvent *event,
gboolean capture)
{
gboolean retval = TRUE;
gint signal_num = -1;
@ -2998,50 +3019,63 @@ clutter_actor_event (ClutterActor *actor,
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);

View File

@ -211,6 +211,8 @@ struct _ClutterActorClass
ClutterCrossingEvent *event);
void (* leave) (ClutterActor *actor,
ClutterCrossingEvent *event);
void (* captured) (ClutterActor *actor,
ClutterEvent *event);
void (* focus_in) (ClutterActor *actor);
void (* focus_out) (ClutterActor *actor);
@ -367,7 +369,8 @@ void clutter_actor_apply_transform_to_point (ClutterActor
/* 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);

View File

@ -379,10 +379,8 @@ clutter_event_free (ClutterEvent *event)
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);
}
}

View File

@ -1133,6 +1133,56 @@ event_click_count_generate (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.
@ -1151,6 +1201,7 @@ clutter_do_event (ClutterEvent *event)
ClutterMainContext *context;
ClutterBackend *backend;
ClutterActor *stage;
static ClutterActor *motion_last_actor = NULL;
context = clutter_context_get_default ();
@ -1175,18 +1226,7 @@ clutter_do_event (ClutterEvent *event)
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:
@ -1203,25 +1243,16 @@ clutter_do_event (ClutterEvent *event)
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:
@ -1246,15 +1277,20 @@ clutter_do_event (ClutterEvent *event)
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)
@ -1270,7 +1306,8 @@ clutter_do_event (ClutterEvent *event)
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 */
@ -1280,7 +1317,7 @@ clutter_do_event (ClutterEvent *event)
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);
@ -1291,27 +1328,7 @@ clutter_do_event (ClutterEvent *event)
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:

View File

@ -828,7 +828,7 @@ clutter_stage_event (ClutterStage *stage,
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)

View File

@ -42,6 +42,18 @@ red_button_cb (ClutterActor *actor,
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
@ -152,6 +164,7 @@ main (int argc, char *argv[])
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",
@ -161,6 +174,8 @@ main (int argc, char *argv[])
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);
@ -193,6 +208,8 @@ main (int argc, char *argv[])
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);