1
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>3. Detecting mouse scrolling on an actor</title><link rel="stylesheet" type="text/css" href="style.css"><meta name="generator" content="DocBook XSL Stylesheets V1.76.1"><link rel="home" href="index.html" title="The Clutter Cookbook"><link rel="up" href="events.html" title="Chapter 3. Events"><link rel="prev" href="events-handling-key-events.html" title="2. Handling key events"><link rel="next" href="events-pointer-motion.html" title="4. Detecting pointer movements on an actor"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">3. Detecting mouse scrolling on an actor</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="events-handling-key-events.html">Prev</a> </td><th width="60%" align="center">Chapter 3. Events</th><td width="20%" align="right"> <a accesskey="n" href="events-pointer-motion.html">Next</a></td></tr></table><hr></div><div class="section" title="3. Detecting mouse scrolling on an actor"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="events-mouse-scroll"></a>3. Detecting mouse scrolling on an actor</h2></div></div></div><div class="section" title="3.1. Problem"><div class="titlepage"><div><div><h3 class="title"><a name="idp10226720"></a>3.1. Problem</h3></div></div></div><p>You want to detect when the mouse is scrolled on an
2
actor (e.g. the pointer is over an actor when a mouse
3
wheel is scrolled).</p></div><div class="section" title="3.2. Solution"><div class="titlepage"><div><div><h3 class="title"><a name="idp10228192"></a>3.2. Solution</h3></div></div></div><p>Connect a callback handler to the <code class="code">scroll-event</code>
4
signal of an actor.</p><p>First, ensure that the actor is reactive (i.e. will
5
respond to events):</p><div class="informalexample"><pre class="programlisting">clutter_actor_set_reactive (actor, TRUE);</pre></div><p>Next, create a callback handler to examine the scroll
6
event and respond to it:</p><div class="informalexample"><pre class="programlisting">static gboolean
7
_scroll_event_cb (ClutterActor *actor,
11
/* determine the direction the mouse was scrolled */
12
ClutterScrollDirection direction;
13
direction = clutter_event_get_scroll_direction (event);
15
/* replace these stubs with real code to move the actor etc. */
18
case CLUTTER_SCROLL_UP:
19
g_debug ("Scrolled up");
21
case CLUTTER_SCROLL_DOWN:
22
g_debug ("Scrolled down");
24
case CLUTTER_SCROLL_RIGHT:
25
g_debug ("Scrolled right");
27
case CLUTTER_SCROLL_LEFT:
28
g_debug ("Scrolled left");
32
return CLUTTER_EVENT_STOP; /* event has been handled */
33
}</pre></div><p>Finally, connect the callback handler to the
34
<code class="code">scroll-event</code> signal of the actor:</p><div class="informalexample"><pre class="programlisting">g_signal_connect (actor,
36
G_CALLBACK (_scroll_event_cb),
37
NULL);</pre></div></div><div class="section" title="3.3. Discussion"><div class="titlepage"><div><div><h3 class="title"><a name="idp10236064"></a>3.3. Discussion</h3></div></div></div><p>A standard mouse wheel will only return up and
38
down movements; but in cases where the mouse has left and
39
right scrolling (e.g. a trackball mouse or trackpad), left and
40
right scroll events may also be emitted.</p><div class="section" title="3.3.1. Creating a scrolling viewport for an actor"><div class="titlepage"><div><div><h4 class="title"><a name="idp10237456"></a>3.3.1. Creating a scrolling viewport for an actor</h4></div></div></div><p>While the simple outline above explains the basics
41
of how to connect to scroll events, it doesn't do much to
42
help with <span class="emphasis"><em>really</em></span> implementing scrolling
43
over an actor. That's what we'll do in this section.</p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>The full code for the example we'll walk through here is
44
available in <a class="link" href="events-mouse-scroll.html#events-mouse-scroll-example" title="Example 3.1. Mouse scrolling over a ClutterActor">this later
45
section</a>.</p></div><p>Scrolling over an actor actually requires coordination
46
between two components:</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p title="Scrollable actor"><b>Scrollable actor. </b>An actor which is too large to fit on the stage
47
or inside the area of the UI assigned to it (otherwise
48
there's no need to scroll over it...).</p></li><li class="listitem"><p title="Viewport"><b>Viewport. </b>This displays a cropped view of part of the scrollable
49
actor, revealing different parts of it as scroll events
50
occur.</p></li></ol></div><p>Here are the steps required to set up the two actors:</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>Create the scrollable actor; it should be larger
51
than the viewport. This example uses a <span class="type">ClutterTexture</span>,
52
but any <span class="type">ClutterActor</span> will work:</p><div class="informalexample"><pre class="programlisting">/* get image file path, set up stage etc. */
54
ClutterActor *texture;
55
texture = clutter_texture_new ();
56
clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (texture),
60
* set the texture's height so it's as tall as the stage
61
* (STAGE_HEIGHT is define'd at the top of the file)
63
clutter_actor_set_request_mode (texture, CLUTTER_REQUEST_WIDTH_FOR_HEIGHT);
64
clutter_actor_set_height (texture, STAGE_HEIGHT);
67
* load the image file;
68
* see <a class="link" href="textures-aspect-ratio.html" title="3. Maintaining the aspect ratio when loading an image into a texture">this recipe</a> for more about loading images into textures
70
clutter_texture_set_from_file (CLUTTER_TEXTURE (texture),
72
NULL);</pre></div></li><li class="listitem"><p>Create the viewport. The simplest way to do
73
this is with a <span class="type">ClutterGroup</span>:</p><div class="informalexample"><pre class="programlisting">ClutterActor *viewport;
74
viewport = clutter_group_new ();
76
/* viewport is _shorter_ than the stage (and the texture) */
77
clutter_actor_set_size (viewport, STAGE_WIDTH, STAGE_HEIGHT * 0.5);
79
/* align the viewport to the center of the stage's y axis */
80
clutter_actor_add_constraint (viewport,
81
clutter_align_constraint_new (stage, CLUTTER_BIND_Y, 0.5));
83
/* viewport needs to respond to scroll events */
84
clutter_actor_set_reactive (viewport, TRUE);
86
/* clip all actors inside the viewport to that group's allocation */
87
clutter_actor_set_clip_to_allocation (viewport, TRUE);</pre></div><p>The key here is calling
88
<code class="code">clutter_actor_set_clip_to_allocation (viewport, TRUE)</code>.
89
This configures the <code class="varname">viewport</code> group so
90
that any of its children are clipped: i.e. only parts of
91
its children which fit inside its allocation are visible. This
92
in turn requires setting an explicit size on the group,
93
rather than allowing it to size itself to fit its
94
children (the latter is the default).</p></li><li class="listitem"><p>Put the scrollable actor into the viewport; and
95
the viewport into its container (in this case,
96
the default stage):</p><div class="informalexample"><pre class="programlisting">clutter_actor_add_child (viewport, texture);
98
clutter_actor_add_child (stage, viewport);</pre></div></li><li class="listitem"><p>Create a callback handler for <code class="code">scroll-event</code>
99
signals emitted by the viewport:</p><div class="informalexample"><pre class="programlisting">static gboolean
100
_scroll_event_cb (ClutterActor *viewport,
104
ClutterActor *scrollable = CLUTTER_ACTOR (user_data);
106
gfloat viewport_height = clutter_actor_get_height (viewport);
107
gfloat scrollable_height = clutter_actor_get_height (scrollable);
109
/* no need to scroll if the scrollable is shorter than the viewport */
110
if (scrollable_height < viewport_height)
111
return CLUTTER_EVENT_STOP;
113
gfloat y = clutter_actor_get_y (scrollable);
115
ClutterScrollDirection direction;
116
direction = clutter_event_get_scroll_direction (event);
120
case CLUTTER_SCROLL_UP:
123
case CLUTTER_SCROLL_DOWN:
127
/* we're only interested in up and down */
128
case CLUTTER_SCROLL_LEFT:
129
case CLUTTER_SCROLL_RIGHT:
134
* the CLAMP macro returns a value for the first argument
135
* that falls within the range specified by the second and
138
* we allow the scrollable's y position to be decremented to the point
139
* where its base is aligned with the base of the viewport
142
viewport_height - scrollable_height,
145
/* animate the change to the scrollable's y coordinate */
146
clutter_actor_animate (scrollable,
147
CLUTTER_EASE_OUT_CUBIC,
152
return CLUTTER_EVENT_STOP;
153
}</pre></div><p>The approach taken here is to move the scrollable
154
actor up, relative to the viewport. Initially, the
155
scrollable will have a <code class="code">y</code> coordinate value
156
of <code class="code">0.0</code> (aligned to the top of the viewport).
157
Scrolling up decrements the
158
<code class="code">y</code> coordinate (down to a minumum of
159
<code class="code">viewport_height - scrollable_height</code>). This moves
160
the top of the scrollable actor "outside" the clip area of the
161
viewport; simultaneously, more of the bottom part of the
162
scrollable moves into the clip area, becoming visible.</p><p>Scrolling down increments the <code class="code">y</code> coordinate
163
(but only up to a maximum value of <code class="code">0.0</code>).</p><p>To see how this works in practice, look at
164
<a class="link" href="events-mouse-scroll.html#events-mouse-scroll-example" title="Example 3.1. Mouse scrolling over a ClutterActor">the code
165
sample</a>. There, the height of the scrollable actor is
166
set to <code class="code">300</code> and the height of the viewport to
167
<code class="code">150</code>. This means that the <code class="code">y</code>
168
coordinate value for the scrollable actor will vary between
169
<code class="code">-150.0</code>: <code class="code">150</code> (the viewport's height)
170
<code class="code">- 300</code> (the scrollable actor's height), making
171
its base visible and clipping its top; and
172
<code class="code">0.0</code>, where its top is visible and its base
173
clipped.</p></li><li class="listitem"><p>Connect the callback handler to the signal; note
174
that we pass the scrollable actor (the texture) to the callback,
175
as we're moving the texture relative to the viewport to
176
create the scrolling effect:</p><div class="informalexample"><pre class="programlisting">g_signal_connect (viewport,
178
G_CALLBACK (_scroll_event_cb),
179
texture);</pre></div></li></ol></div><p>Here's a video of the result:</p><p><video controls="controls" src="videos/events-mouse-scroll.ogv"><a href="videos/events-mouse-scroll.ogv"></a></video></p></div></div><div class="section" title="3.4. Full example"><div class="titlepage"><div><div><h3 class="title"><a name="idp10276528"></a>3.4. Full example</h3></div></div></div><div class="example"><a name="events-mouse-scroll-example"></a><p class="title"><b>Example 3.1. Mouse scrolling over a <span class="type">ClutterActor</span></b></p><div class="example-contents"><pre class="programlisting">#include <clutter/clutter.h>
181
#define STAGE_HEIGHT 300
182
#define STAGE_WIDTH STAGE_HEIGHT
183
#define SCROLL_AMOUNT STAGE_HEIGHT * 0.125
186
_scroll_event_cb (ClutterActor *viewport,
190
ClutterActor *scrollable = CLUTTER_ACTOR (user_data);
192
gfloat viewport_height = clutter_actor_get_height (viewport);
193
gfloat scrollable_height = clutter_actor_get_height (scrollable);
195
ClutterScrollDirection direction;
197
/* no need to scroll if the scrollable is shorter than the viewport */
198
if (scrollable_height < viewport_height)
201
y = clutter_actor_get_y (scrollable);
203
direction = clutter_event_get_scroll_direction (event);
207
case CLUTTER_SCROLL_UP:
210
case CLUTTER_SCROLL_DOWN:
214
/* we're only interested in up and down */
215
case CLUTTER_SCROLL_LEFT:
216
case CLUTTER_SCROLL_RIGHT:
221
* the CLAMP macro returns a value for the first argument
222
* that falls within the range specified by the second and
225
* we allow the scrollable's y position to be decremented to the point
226
* where its base is aligned with the base of the viewport
229
viewport_height - scrollable_height,
232
/* animate the change to the scrollable's y coordinate */
233
clutter_actor_animate (scrollable,
234
CLUTTER_EASE_OUT_CUBIC,
243
main (int argc, char *argv[])
246
ClutterActor *viewport;
247
ClutterActor *texture;
249
gchar *image_file_path = TESTS_DATA_DIR "/redhand.png";
253
image_file_path = argv[1];
256
if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
259
stage = clutter_stage_new ();
260
clutter_actor_set_size (stage, STAGE_WIDTH, STAGE_HEIGHT);
261
g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
263
/* the scrollable actor */
264
texture = clutter_texture_new ();
265
clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (texture),
268
/* set the texture's height so it's as tall as the stage */
269
clutter_actor_set_request_mode (texture, CLUTTER_REQUEST_WIDTH_FOR_HEIGHT);
270
clutter_actor_set_height (texture, STAGE_HEIGHT);
272
clutter_texture_set_from_file (CLUTTER_TEXTURE (texture),
276
/* the viewport which the box is scrolled within */
277
viewport = clutter_actor_new ();
279
/* viewport is shorter than the stage */
280
clutter_actor_set_size (viewport, STAGE_WIDTH, STAGE_HEIGHT * 0.5);
282
/* align the viewport to the center of the stage's y axis */
283
clutter_actor_add_constraint (viewport, clutter_align_constraint_new (stage, CLUTTER_BIND_Y, 0.5));
285
/* viewport needs to respond to scroll events */
286
clutter_actor_set_reactive (viewport, TRUE);
288
/* clip all actors inside the viewport to that group's allocation */
289
clutter_actor_set_clip_to_allocation (viewport, TRUE);
291
/* put the texture inside the viewport */
292
clutter_actor_add_child (viewport, texture);
294
/* add the viewport to the stage */
295
clutter_actor_add_child (stage, viewport);
297
g_signal_connect (viewport,
299
G_CALLBACK (_scroll_event_cb),
302
clutter_actor_show (stage);
308
</pre></div></div><br class="example-break"></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="events-handling-key-events.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="events.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="events-pointer-motion.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">2. Handling key events </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> 4. Detecting pointer movements on an actor</td></tr></table></div></body></html>