mirror of
https://github.com/brl/mutter.git
synced 2024-11-23 00:20:42 -05:00
cookbook: Add recipe for key event handling
Attached patch contains a cookbook recipe about key press event handling. It covers both a simple approach (connecting a callback to a key-press-event signal which manually analyses the key and modifiers), and a more complicated one based on a binding pool. There's also some discussion of the two approaches. http://bugzilla.clutter-project.org/show_bug.cgi?id=2162 Signed-off-by: Emmanuele Bassi <ebassi@linux.intel.com>
This commit is contained in:
parent
30ca03de1d
commit
566f75d97f
@ -386,6 +386,300 @@ on_paint (ClutterActor *actor)
|
||||
|
||||
</chapter> <!-- actors }}} -->
|
||||
|
||||
<chapter id="events">
|
||||
<title>Events</title>
|
||||
|
||||
<epigraph>
|
||||
<attribution></attribution>
|
||||
<para></para>
|
||||
</epigraph>
|
||||
|
||||
<section id="events-introduction">
|
||||
<title>Introduction</title>
|
||||
<para>introduction</para>
|
||||
</section>
|
||||
|
||||
<section id="events-handling-key-events">
|
||||
<title>Handling key events</title>
|
||||
|
||||
<section>
|
||||
<title>Problem</title>
|
||||
<para>You want to respond to key presses on an actor.</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Solutions</title>
|
||||
|
||||
<para>There are two possible solutions:</para>
|
||||
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<para><emphasis>Solution 1:</emphasis> Connect a callback to the
|
||||
actor; inside the callback, manually analyse which key and
|
||||
modifier(s) were pressed and react accordingly.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><emphasis>Solution 2:</emphasis> Use an actor's
|
||||
ClutterBindingPool to declaratively assign actions to specific
|
||||
key and modifier combinations.</para>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
|
||||
<para>Each solution is covered below.</para>
|
||||
|
||||
<section>
|
||||
<title>Solution 1</title>
|
||||
|
||||
<para>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.</para>
|
||||
|
||||
<para>First, connect an actor's key-press-event signal
|
||||
to a callback:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
g_signal_connect (actor,
|
||||
"key-press-event",
|
||||
G_CALLBACK (_key_press_cb),
|
||||
NULL);
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>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:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>Note that Clutter provides a range of key value definitions
|
||||
(like CLUTTER_Up, used above). These are generated from the list in the
|
||||
<ulink url="http://cgit.freedesktop.org/xorg/proto/x11proto/plain/keysymdef.h">X.Org source code</ulink>
|
||||
(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).</para>
|
||||
|
||||
<para>CLUTTER_SHIFT_MASK, CLUTTER_CONTROL_MASK and other modifiers are
|
||||
defined in the ClutterModifierType enum.</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Solution 2</title>
|
||||
|
||||
<para>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.</para>
|
||||
|
||||
<para>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.</para>
|
||||
|
||||
<para>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:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
ClutterBindingPool *binding_pool;
|
||||
binding_pool = clutter_binding_pool_get_for_class (CLUTTER_STAGE_GET_CLASS (stage));
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>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:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
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);
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>Another example, binding up + Shift + Control to an action which
|
||||
calls _move_up_shift_control when activated:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
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);
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>The function called when an action is activated looks like this
|
||||
(for _move_up):</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
static void
|
||||
_move_up (GObject *instance,
|
||||
const gchar *action_name,
|
||||
guint key_val,
|
||||
ClutterModifierType modifiers,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_debug ("Up pressed");
|
||||
}
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>Then bind the key-press-event for the actor (in our case,
|
||||
the stage) to a callback:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
g_signal_connect (stage,
|
||||
"key-press-event",
|
||||
G_CALLBACK (_key_press_cb),
|
||||
NULL);
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>Finally, inside the callback, pass control to the actor's
|
||||
binding pool rather than dissecting the key press event
|
||||
yourself:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
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));
|
||||
}
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>Now, when a key plus modifiers that has been bound to an action
|
||||
is pressed on the actor, the appropriate action is activated.</para>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Discussion</title>
|
||||
|
||||
<section>
|
||||
<title>Pros and cons of Solution 1 and Solution 2</title>
|
||||
|
||||
<para>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.</para>
|
||||
|
||||
<para>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.</para>
|
||||
|
||||
<para>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).</para>
|
||||
|
||||
<para>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.</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Other useful things to know about key press events</title>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>A ClutterKeyEvent contains only a <emphasis>single</emphasis>
|
||||
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.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>By default, the stage receives all key events.
|
||||
To make another actor receive key events, use
|
||||
clutter_stage_set_key_focus:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
/*
|
||||
* stage is a ClutterStage instance;
|
||||
* actor is the ClutterActor instance which should receive key events
|
||||
*/
|
||||
clutter_stage_set_key_focus (stage, actor);
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
</section>
|
||||
</chapter> <!-- }}} events -->
|
||||
|
||||
<chapter id="textures"> <!-- textures {{{ -->
|
||||
<title>Textures</title>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user