~ubuntu-branches/ubuntu/vivid/clutter-1.0/vivid-proposed

« back to all changes in this revision

Viewing changes to doc/cookbook/html/events-mouse-scroll.html

  • Committer: Package Import Robot
  • Author(s): Sjoerd Simons
  • Date: 2013-03-15 23:20:40 UTC
  • mto: (4.1.25 experimental) (1.5.1)
  • mto: This revision was merged to the branch mainline in revision 23.
  • Revision ID: package-import@ubuntu.com-20130315232040-9wwd4f9mgx8lewoz
Tags: upstream-1.13.8
Import upstream version 1.13.8

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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,
 
8
                  ClutterEvent *event,
 
9
                  gpointer      user_data)
 
10
{
 
11
  /* determine the direction the mouse was scrolled */
 
12
  ClutterScrollDirection direction;
 
13
  direction = clutter_event_get_scroll_direction (event);
 
14
 
 
15
  /* replace these stubs with real code to move the actor etc. */
 
16
  switch (direction)
 
17
    {
 
18
    case CLUTTER_SCROLL_UP:
 
19
      g_debug ("Scrolled up");
 
20
      break;
 
21
    case CLUTTER_SCROLL_DOWN:
 
22
      g_debug ("Scrolled down");
 
23
      break;
 
24
    case CLUTTER_SCROLL_RIGHT:
 
25
      g_debug ("Scrolled right");
 
26
      break;
 
27
    case CLUTTER_SCROLL_LEFT:
 
28
      g_debug ("Scrolled left");
 
29
      break;
 
30
    }
 
31
 
 
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,
 
35
                  "scroll-event",
 
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. */
 
53
 
 
54
ClutterActor *texture;
 
55
texture = clutter_texture_new ();
 
56
clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (texture),
 
57
                                       TRUE);
 
58
 
 
59
/*
 
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)
 
62
 */
 
63
clutter_actor_set_request_mode (texture, CLUTTER_REQUEST_WIDTH_FOR_HEIGHT);
 
64
clutter_actor_set_height (texture, STAGE_HEIGHT);
 
65
 
 
66
/*
 
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
 
69
 */
 
70
clutter_texture_set_from_file (CLUTTER_TEXTURE (texture),
 
71
                               image_file_path,
 
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 ();
 
75
 
 
76
/* viewport is _shorter_ than the stage (and the texture) */
 
77
clutter_actor_set_size (viewport, STAGE_WIDTH, STAGE_HEIGHT * 0.5);
 
78
 
 
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));
 
82
 
 
83
/* viewport needs to respond to scroll events */
 
84
clutter_actor_set_reactive (viewport, TRUE);
 
85
 
 
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);
 
97
 
 
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,
 
101
                  ClutterEvent *event,
 
102
                  gpointer      user_data)
 
103
{
 
104
  ClutterActor *scrollable = CLUTTER_ACTOR (user_data);
 
105
 
 
106
  gfloat viewport_height = clutter_actor_get_height (viewport);
 
107
  gfloat scrollable_height = clutter_actor_get_height (scrollable);
 
108
 
 
109
  /* no need to scroll if the scrollable is shorter than the viewport */
 
110
  if (scrollable_height &lt; viewport_height)
 
111
    return CLUTTER_EVENT_STOP;
 
112
 
 
113
  gfloat y = clutter_actor_get_y (scrollable);
 
114
 
 
115
  ClutterScrollDirection direction;
 
116
  direction = clutter_event_get_scroll_direction (event);
 
117
 
 
118
  switch (direction)
 
119
    {
 
120
    case CLUTTER_SCROLL_UP:
 
121
      y -= SCROLL_AMOUNT;
 
122
      break;
 
123
    case CLUTTER_SCROLL_DOWN:
 
124
      y += SCROLL_AMOUNT;
 
125
      break;
 
126
 
 
127
    /* we're only interested in up and down */
 
128
    case CLUTTER_SCROLL_LEFT:
 
129
    case CLUTTER_SCROLL_RIGHT:
 
130
      break;
 
131
    }
 
132
 
 
133
  /*
 
134
   * the CLAMP macro returns a value for the first argument
 
135
   * that falls within the range specified by the second and
 
136
   * third arguments
 
137
   *
 
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
 
140
   */
 
141
  y = CLAMP (y,
 
142
             viewport_height - scrollable_height,
 
143
             0.0);
 
144
 
 
145
  /* animate the change to the scrollable's y coordinate */
 
146
  clutter_actor_animate (scrollable,
 
147
                         CLUTTER_EASE_OUT_CUBIC,
 
148
                         300,
 
149
                         "y", y,
 
150
                         NULL);
 
151
 
 
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,
 
177
                  "scroll-event",
 
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 &lt;clutter/clutter.h&gt;
 
180
 
 
181
#define STAGE_HEIGHT 300
 
182
#define STAGE_WIDTH STAGE_HEIGHT
 
183
#define SCROLL_AMOUNT STAGE_HEIGHT * 0.125
 
184
 
 
185
static gboolean
 
186
_scroll_event_cb (ClutterActor *viewport,
 
187
                  ClutterEvent *event,
 
188
                  gpointer      user_data)
 
189
{
 
190
  ClutterActor *scrollable = CLUTTER_ACTOR (user_data);
 
191
 
 
192
  gfloat viewport_height = clutter_actor_get_height (viewport);
 
193
  gfloat scrollable_height = clutter_actor_get_height (scrollable);
 
194
  gfloat y;
 
195
  ClutterScrollDirection direction;
 
196
 
 
197
  /* no need to scroll if the scrollable is shorter than the viewport */
 
198
  if (scrollable_height &lt; viewport_height)
 
199
    return TRUE;
 
200
 
 
201
  y = clutter_actor_get_y (scrollable);
 
202
 
 
203
  direction = clutter_event_get_scroll_direction (event);
 
204
 
 
205
  switch (direction)
 
206
    {
 
207
    case CLUTTER_SCROLL_UP:
 
208
      y -= SCROLL_AMOUNT;
 
209
      break;
 
210
    case CLUTTER_SCROLL_DOWN:
 
211
      y += SCROLL_AMOUNT;
 
212
      break;
 
213
 
 
214
    /* we're only interested in up and down */
 
215
    case CLUTTER_SCROLL_LEFT:
 
216
    case CLUTTER_SCROLL_RIGHT:
 
217
      break;
 
218
    }
 
219
 
 
220
  /*
 
221
   * the CLAMP macro returns a value for the first argument
 
222
   * that falls within the range specified by the second and
 
223
   * third arguments
 
224
   *
 
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
 
227
   */
 
228
  y = CLAMP (y,
 
229
             viewport_height - scrollable_height,
 
230
             0.0);
 
231
 
 
232
  /* animate the change to the scrollable's y coordinate */
 
233
  clutter_actor_animate (scrollable,
 
234
                         CLUTTER_EASE_OUT_CUBIC,
 
235
                         300,
 
236
                         "y", y,
 
237
                         NULL);
 
238
 
 
239
  return TRUE;
 
240
}
 
241
 
 
242
int
 
243
main (int argc, char *argv[])
 
244
{
 
245
  ClutterActor *stage;
 
246
  ClutterActor *viewport;
 
247
  ClutterActor *texture;
 
248
 
 
249
  gchar *image_file_path = TESTS_DATA_DIR "/redhand.png";
 
250
 
 
251
  if (argc &gt; 1)
 
252
    {
 
253
      image_file_path = argv[1];
 
254
    }
 
255
 
 
256
  if (clutter_init (&amp;argc, &amp;argv) != CLUTTER_INIT_SUCCESS)
 
257
    return 1;
 
258
 
 
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);
 
262
 
 
263
  /* the scrollable actor */
 
264
  texture = clutter_texture_new ();
 
265
  clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (texture),
 
266
                                         TRUE);
 
267
 
 
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);
 
271
 
 
272
  clutter_texture_set_from_file (CLUTTER_TEXTURE (texture),
 
273
                                 image_file_path,
 
274
                                 NULL);
 
275
 
 
276
  /* the viewport which the box is scrolled within */
 
277
  viewport = clutter_actor_new ();
 
278
 
 
279
  /* viewport is shorter than the stage */
 
280
  clutter_actor_set_size (viewport, STAGE_WIDTH, STAGE_HEIGHT * 0.5);
 
281
 
 
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));
 
284
 
 
285
  /* viewport needs to respond to scroll events */
 
286
  clutter_actor_set_reactive (viewport, TRUE);
 
287
 
 
288
  /* clip all actors inside the viewport to that group's allocation */
 
289
  clutter_actor_set_clip_to_allocation (viewport, TRUE);
 
290
 
 
291
  /* put the texture inside the viewport */
 
292
  clutter_actor_add_child (viewport, texture);
 
293
 
 
294
  /* add the viewport to the stage */
 
295
  clutter_actor_add_child (stage, viewport);
 
296
 
 
297
  g_signal_connect (viewport,
 
298
                    "scroll-event",
 
299
                    G_CALLBACK (_scroll_event_cb),
 
300
                    texture);
 
301
 
 
302
  clutter_actor_show (stage);
 
303
 
 
304
  clutter_main ();
 
305
 
 
306
  return 0;
 
307
}
 
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>