diff --git a/doc/cookbook/clutter-cookbook.xml.in b/doc/cookbook/clutter-cookbook.xml.in index 0146bfadb..e6c9297de 100644 --- a/doc/cookbook/clutter-cookbook.xml.in +++ b/doc/cookbook/clutter-cookbook.xml.in @@ -386,6 +386,300 @@ on_paint (ClutterActor *actor) + + Events + + + + + + +
+ Introduction + introduction +
+ +
+ Handling key events + +
+ Problem + You want to respond to key presses on an actor. +
+ +
+ Solutions + + There are two possible solutions: + + + + Solution 1: Connect a callback to the + actor; inside the callback, manually analyse which key and + modifier(s) were pressed and react accordingly. + + + Solution 2: Use an actor's + ClutterBindingPool to declaratively assign actions to specific + key and modifier combinations. + + + + Each solution is covered below. + +
+ Solution 1 + + Connect the key-press-event signal for an actor to a callback; + then examine the event in the callback to determine which key and + modifiers were pressed. + + First, connect an actor's key-press-event signal + to a callback: + + + +g_signal_connect (actor, + "key-press-event", + G_CALLBACK (_key_press_cb), + NULL); + + + + Then, in the callback, check which key was pressed and which + modifiers were down at the same time. For example, this callback + checks for a press on the up arrow key and whether + the Shift and/or Control key were down: + + + +static void +_key_press_cb (ClutterActor *actor, + ClutterEvent *event, + gpointer user_data) +{ + guint keyval = clutter_event_get_key_symbol (event); + + if (CLUTTER_Up == keyval) + { + ClutterModifierType modifiers = clutter_event_get_state (event); + + switch (modifiers) + { + case CLUTTER_SHIFT_MASK: + g_debug ("Up and shift pressed"); + break; + + case CLUTTER_SHIFT_MASK + CLUTTER_CONTROL_MASK: + g_debug ("Up and shift and control pressed"); + break; + + default: + g_debug ("Up pressed"); + break; + } + } +} + + + + Note that Clutter provides a range of key value definitions + (like CLUTTER_Up, used above). These are generated from the list in the + X.Org source code + (replace "XK" with "CLUTTER" in the definitions there to get the + CLUTTER equivalents; alternatively, look at the clutter-keysyms.h + header file for the list). + + CLUTTER_SHIFT_MASK, CLUTTER_CONTROL_MASK and other modifiers are + defined in the ClutterModifierType enum. + +
+ +
+ Solution 2 + + Assign actions to an actor's ClutterBindingPool. + A binding pool stores mappings from a key press (either a single key + or a key plus modifiers) to actions; an action is simply a callback + function with a specific signature. + + While this approach is trickier to implement, it is more + flexible and removes the drudgery of writing branching code to + handle different key presses. See the Discussion section for + more details. + + To use this approach with an actor which will receive key press + events, first get that actor's binding pool. In the example below, + we're using the binding pool for the default ClutterStage: + + + +ClutterBindingPool *binding_pool; +binding_pool = clutter_binding_pool_get_for_class (CLUTTER_STAGE_GET_CLASS (stage)); + + + + Next, install actions into the binding pool. For example, to + install an action bound to the up arrow key, which calls the _move_up + function when that key is pressed, you would do: + + + +clutter_binding_pool_install_action (binding_pool, + "move-up", /* identifier */ + CLUTTER_Up, /* up arrow pressed */ + 0, /* no modifiers pressed */ + G_CALLBACK (_move_up), + NULL, /* no user data passed */ + NULL); + + + + Another example, binding up + Shift + Control to an action which + calls _move_up_shift_control when activated: + + + +clutter_binding_pool_install_action (binding_pool, + "move-up-shift-control", + CLUTTER_Up, + CLUTTER_SHIFT_MASK + CLUTTER_CONTROL_MASK, + G_CALLBACK (_move_up_shift_control), + NULL, + NULL); + + + + The function called when an action is activated looks like this + (for _move_up): + + + +static void +_move_up (GObject *instance, + const gchar *action_name, + guint key_val, + ClutterModifierType modifiers, + gpointer user_data) +{ + g_debug ("Up pressed"); +} + + + + Then bind the key-press-event for the actor (in our case, + the stage) to a callback: + + + +g_signal_connect (stage, + "key-press-event", + G_CALLBACK (_key_press_cb), + NULL); + + + + Finally, inside the callback, pass control to the actor's + binding pool rather than dissecting the key press event + yourself: + + + +static gboolean +_key_press_cb (ClutterActor *actor, + ClutterEvent *event, + gpointer user_data) +{ + ClutterBindingPool *pool; + + pool = clutter_binding_pool_find (G_OBJECT_TYPE_NAME (actor)); + + return clutter_binding_pool_activate (pool, + clutter_event_get_key_symbol (event), + clutter_event_get_state (event), + G_OBJECT (actor)); +} + + + + Now, when a key plus modifiers that has been bound to an action + is pressed on the actor, the appropriate action is activated. + +
+
+ +
+ Discussion + +
+ Pros and cons of Solution 1 and Solution 2 + + Solution 1 is the simplest (in terms of the amount of code you + have to write for simple cases), but could quickly turn into a mess if + you need many conditions or want to capture many key combinations. + Also, if multiple actors need to respond to key press events, you'll + need similar event dissection code in each callback. + + Solution 2 is more complicated to implement, but scales better + if you have many different key combinations on multiple actors. + The binding pool protects you from the minutiae of detecting which + keys were pressed, leaving you to concentrate on the + triggered actions instead. This could simplify your control + logic. + + In addition, Solution 2 lets you write a single callback to + handle all key press events for all actors. This callback could then + use clutter_binding_pool_find (as in the example code) to determine + which binding pool to activate (depending on which actor received the + key press event). + + Finally, a binding pool allows you to block and unblock actions. + This means you can make the response to a key press event conditional + on application state. For example, let's say you wanted the up arrow + key to move an actor, but only when the actor is at the bottom + of the stage. To implement this, you could disable the up arrow key + action in the binding pool initially; then, once the actor reaches the + bottom of the stage, enable the up arrow key action again. While this + is possible with Solution 1, you would have to implement more of the + state management code yourself. +
+ +
+ Other useful things to know about key press events + + + + A ClutterKeyEvent contains only a single + key value, plus possibly one or more modifier keys (like Shift, + Control, Alt etc.). There are no functions in the + Clutter API which return events for tracking near-simultaneous + presses on multiple keys. + + + + By default, the stage receives all key events. + To make another actor receive key events, use + clutter_stage_set_key_focus: + + + +/* + * stage is a ClutterStage instance; + * actor is the ClutterActor instance which should receive key events + */ +clutter_stage_set_key_focus (stage, actor); + + + + + +
+ +
+ +
+
+ Textures