mirror of
https://github.com/brl/mutter.git
synced 2024-12-23 19:42:05 +00:00
cookbook: Add recipe about handling button events
Recipe covers adding handlers for button-press-event and button-release-event signals on actors, and how to examine the content of a ClutterButtonEvent via API functions. The discussion section explains about click count (the criteria for how clicks get counted, including distance and time settings); how button numbers are reported; and how to use ClutterClickAction as an alternative for press + release in certain scenarios.
This commit is contained in:
parent
94439e5526
commit
cb191ff6f1
@ -1021,4 +1021,392 @@ ClutterStage *stage = clutter_event_get_stage (event);
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
<section id="events-buttons">
|
||||
<title>Making an actor respond to button events</title>
|
||||
|
||||
<section>
|
||||
<title>Problem</title>
|
||||
|
||||
<para>You want an actor to respond to button events. These might
|
||||
be buttons on an input device like a mouse;
|
||||
or input events caused by other means, like touches on a screen.</para>
|
||||
|
||||
<para>Some examples of where this is useful:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>For implementing button widgets which respond to
|
||||
button clicks.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>To make actor selections by mouse click (e.g.
|
||||
as part of a drawing application).</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>To recognise a button press followed by pointer
|
||||
motion and button release (e.g. to implement drag
|
||||
and drop or kinetic animations).</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Solution</title>
|
||||
|
||||
<para>Connect a handler to the <emphasis>button-press-event</emphasis>
|
||||
and/or <emphasis>button-release-event</emphasis> signals of an
|
||||
actor.</para>
|
||||
|
||||
<note>
|
||||
<para>The <emphasis>button-press-event</emphasis> is emitted
|
||||
when a button is pressed (not necessarily released) on a reactive
|
||||
actor; the <emphasis>button-release-event</emphasis> when a
|
||||
button is released on a reactive actor (even if the button was
|
||||
pressed down somewhere else).</para>
|
||||
</note>
|
||||
|
||||
<para>First, ensure the actor is reactive:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
clutter_actor_set_reactive (actor, TRUE);
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>Next, create a function to handle the signal(s) you are
|
||||
interested in. The function signature is the same for both the
|
||||
press and release signals:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
gboolean
|
||||
callback_function (ClutterActor *actor,
|
||||
ClutterEvent *event,
|
||||
gpointer user_data);
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>You can use a single function as the
|
||||
callback for both signals (or write a different one for each signal).
|
||||
Here's an example function which can be used as a callback
|
||||
for both press and release signals, as it simply pulls data
|
||||
out of the event and displays it:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
/* <varname>event</varname> is a <type>ClutterButtonEvent</type>
|
||||
* for both the press and the release signal; it contains
|
||||
* data about where the event occurred
|
||||
*/
|
||||
static gboolean
|
||||
button_event_cb (ClutterActor *actor,
|
||||
ClutterEvent *event,
|
||||
gpointer user_data)
|
||||
{
|
||||
gfloat x, y;
|
||||
gchar *event_type;
|
||||
guint button_pressed;
|
||||
ClutterModifierType state;
|
||||
gchar *ctrl_pressed;
|
||||
guint32 click_count;
|
||||
|
||||
/* where the pointer was (relative to the stage)
|
||||
* when the button event occurred; use
|
||||
* <function>clutter_actor_transform_stage_point()</function>
|
||||
* to transform to actor-relative coordinates
|
||||
*/
|
||||
clutter_event_get_coords (event, &x, &y);
|
||||
|
||||
/* check whether it was a press or release event */
|
||||
event_type = "released";
|
||||
if (clutter_event_type (event) == CLUTTER_BUTTON_PRESS)
|
||||
event_type = "pressed";
|
||||
|
||||
/* which button triggered the event */
|
||||
button_pressed = clutter_event_get_button (event);
|
||||
|
||||
/* keys down when the event occurred;
|
||||
* this is a bit mask composed of the bits for each key held down
|
||||
* when the button was pressed or released; see the
|
||||
* <type>ClutterModifierType</type> enum in the Clutter API docs
|
||||
* for a list of the available modifiers
|
||||
*/
|
||||
state = clutter_event_get_state (event);
|
||||
|
||||
ctrl_pressed = "ctrl not pressed";
|
||||
if (state & CLUTTER_CONTROL_MASK)
|
||||
ctrl_pressed = "ctrl pressed";
|
||||
|
||||
/* click count */
|
||||
click_count = clutter_event_get_click_count (event);
|
||||
|
||||
g_debug ("button %d was %s at %.0f,%.0f; %s; click count %d",
|
||||
button_pressed,
|
||||
event_type,
|
||||
x,
|
||||
y,
|
||||
ctrl_pressed,
|
||||
click_count);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>Finally, connect the signals to the function(s):</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
/* connect the press event */
|
||||
g_signal_connect (actor,
|
||||
"button-press-event",
|
||||
G_CALLBACK (button_event_cb),
|
||||
NULL);
|
||||
|
||||
/* connect the release event */
|
||||
g_signal_connect (actor,
|
||||
"button-release-event",
|
||||
G_CALLBACK (button_event_cb),
|
||||
NULL);
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>Pressing or releasing a button on the actor will now
|
||||
trigger a call to the <function>button_event_cb()</function>
|
||||
function. See <link linkend="events-buttons-example-1">the full
|
||||
example</link> for more details.</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Discussion</title>
|
||||
|
||||
<para>Properties of the <type>ClutterButtonEvent</type>
|
||||
emitted by both signals should be examined using the
|
||||
<function>clutter_event_*</function> functions (rather than struct
|
||||
members directly), as in the example above. While most of these
|
||||
functions are self-explanatory, a couple require more explanation:
|
||||
see the sections below.</para>
|
||||
|
||||
<para>Also covered below is an alternative approach to handling a
|
||||
button press followed by a release on a single actor: by adding
|
||||
a <type>ClutterClickAction</type> to an actor. See
|
||||
<link linkend="events-buttons-clutterclickaction">this section</link>
|
||||
for details.</para>
|
||||
|
||||
<para>Finally, a <link linkend="events-buttons-example-3">longer
|
||||
example</link> is included, showing how to make use of button press,
|
||||
button release and pointer events in a simple drawing application.</para>
|
||||
|
||||
<section>
|
||||
<title>Click count</title>
|
||||
|
||||
<para>The click count records the number of times a press/release
|
||||
pair occurred in sequence. You can retrieve it via the
|
||||
<function>clutter_event_get_click_count()</function> function.</para>
|
||||
|
||||
<note>
|
||||
<para>A press/release pair is effectively a click, so this term
|
||||
will be used from now on throughout this section,
|
||||
to make the explanation simpler. However, the click count has
|
||||
nothing to do with <type>ClutterClickActions</type>, described
|
||||
<link linkend="events-buttons-clutterclickaction">later</link>.
|
||||
</para>
|
||||
</note>
|
||||
|
||||
<para>For clicks to be considered part of the same sequence (for
|
||||
the purposes of counting), all the clicks after the first one
|
||||
must occur within the global <varname>double_click_distance</varname>
|
||||
(pixels) of the first click; and the time between click
|
||||
<code>n</code> and click <code>n+1</code> must be <code><=</code>
|
||||
the global <varname>double_click_time</varname> (milliseconds).</para>
|
||||
|
||||
<para>The clicks <emphasis>do not</emphasis> have to occur on
|
||||
the same actor: providing they occur within the double click
|
||||
distance and time, they are counted as part of the same click
|
||||
sequence. Also note that the clicks don't even have to happen
|
||||
on a reactive actor: providing they happen somewhere on the
|
||||
stage, they will still increment the click count.</para>
|
||||
|
||||
<para>The default double click time and distance are
|
||||
stored in the <type>ClutterSettings</type> associated
|
||||
with an application. You can get/set their values like this:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
gint double_click_distance;
|
||||
gint double_click_time;
|
||||
|
||||
ClutterSettings *settings = clutter_settings_get_default ();
|
||||
|
||||
/* get double click settings */
|
||||
g_object_get (settings,
|
||||
"double-click-distance", &double_click_distance,
|
||||
"double-click-time", &double_click_time,
|
||||
NULL);
|
||||
|
||||
/* set */
|
||||
g_object_set (settings,
|
||||
"double-click-distance", 50,
|
||||
"double-click-time", 1000,
|
||||
NULL);
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Button numbering</title>
|
||||
|
||||
<para><function>clutter_event_get_button()</function> returns
|
||||
an integer representing the pressed or released button.</para>
|
||||
|
||||
<para>In the case of a standard scroll mouse, the numbers
|
||||
returned are reliable across different hardware models:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>1 = left mouse button</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>2 = scroll wheel</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>3 = right mouse button</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>For mice with more buttons, or other types of
|
||||
input devices, the mappings may not be so
|
||||
straightforward: you may have to experiment to see
|
||||
which button returns which value.</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section id="events-buttons-clutterclickaction">
|
||||
<title><type>ClutterClickAction</type></title>
|
||||
|
||||
<para><type>ClutterActions</type> add flexible event handling
|
||||
to <type>ClutterActors</type>. They recognise and abstract
|
||||
common sequences of low-level events into a single, more easily
|
||||
managed high-level event. In the case of a
|
||||
<type>ClutterClickAction</type>, the abstraction is over
|
||||
a press followed by a release on a single actor. This is
|
||||
achieved by "synthesising" the press and release signals on
|
||||
the actor: in other words, the action captures those
|
||||
two signals when emitted by a single actor; and, once captured, the
|
||||
action emits a single <emphasis>clicked</emphasis> signal
|
||||
<emphasis>instead</emphasis> of the two signals being
|
||||
emitted by the actor.</para>
|
||||
|
||||
<para>The pointer can move off the actor between the press and
|
||||
release, but the press and release must both occur on the same
|
||||
actor, with no intervening presses or releases on other
|
||||
actors. In addition, there are no maximum distance or time
|
||||
constraints on the press and release.</para>
|
||||
|
||||
<para>If a press occurs and you want to force it to be released
|
||||
(e.g. to break a pointer grab after a certain length of
|
||||
time has elapsed), use
|
||||
<function>clutter_click_action_release()</function>.</para>
|
||||
|
||||
<para>On the down side, the <emphasis>clicked</emphasis> signal
|
||||
doesn't present the same detailed <type>ClutterButtonEvent</type>
|
||||
to the handler. So, for example, you can't get a click count from a
|
||||
<type>ClutterClickAction</type> (though you could count
|
||||
the clicks yourself, of course); and you don't have access
|
||||
to the coordinates where the press or release occurred.</para>
|
||||
|
||||
<para>To add a click action to a <type>ClutterActor</type>:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
ClutterAction *action = clutter_click_action_new ();
|
||||
clutter_actor_add_action (actor, action);
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<note>
|
||||
<para>An actor must still be set to reactive so that its
|
||||
signals can be routed to a click action.</para>
|
||||
</note>
|
||||
|
||||
<para>Create a handler function (note the function
|
||||
signature is different from the one for the press or
|
||||
releas signal handler):</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
void
|
||||
clicked_cb (ClutterClickAction *action,
|
||||
ClutterActor *actor,
|
||||
gpointer user_data)
|
||||
{
|
||||
/* display the number of the clicked button (equivalent
|
||||
* to the number returned by clutter_event_get_button())
|
||||
*/
|
||||
g_debug ("Button %d clicked", clutter_click_action_get_button (action));
|
||||
}
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>Connect the signal to the handler:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
g_signal_connect (action,
|
||||
"clicked",
|
||||
G_CALLBACK (clicked_cb),
|
||||
NULL);
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>The <link linkend="events-buttons-example-2">example
|
||||
code</link> gives a bit more detail about how to use click
|
||||
actions.</para>
|
||||
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Full examples</title>
|
||||
|
||||
<example id="events-buttons-example-1">
|
||||
<title>Examining properties of a <type>ClutterButtonEvent</type></title>
|
||||
<programlisting>
|
||||
<xi:include href="examples/events-buttons.c" parse="text">
|
||||
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
||||
</xi:include>
|
||||
</programlisting>
|
||||
</example>
|
||||
|
||||
<example id="events-buttons-example-2">
|
||||
<title>Using <type>ClutterClickAction</type> to capture
|
||||
button events on an actor</title>
|
||||
<programlisting>
|
||||
<xi:include href="examples/events-buttons-click.c" parse="text">
|
||||
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
||||
</xi:include>
|
||||
</programlisting>
|
||||
</example>
|
||||
|
||||
<example id="events-buttons-example-3">
|
||||
<title>Using button and pointer events for drawing</title>
|
||||
<para>This code was inspired by
|
||||
<ulink url="http://git.clutter-project.org/cluttersmith/">ClutterSmith</ulink>
|
||||
</para>
|
||||
<programlisting>
|
||||
<xi:include href="examples/events-buttons-lasso.c" parse="text">
|
||||
<xi:fallback>a code sample should be here... but isn't</xi:fallback>
|
||||
</xi:include>
|
||||
</programlisting>
|
||||
</example>
|
||||
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
</chapter>
|
||||
|
Loading…
Reference in New Issue
Block a user