cookbook: Added walk through of code example for mouse scroll

Modified the mouse scroll example to fit better with the tutorial
walkthrough.

Added a stepped walkthrough of the scrollable actor code
example.
This commit is contained in:
Elliot Smith 2010-08-11 16:54:28 +01:00
parent 57ba89c8de
commit f568a68ee1
2 changed files with 243 additions and 14 deletions

View File

@ -435,15 +435,225 @@ _scroll_event_cb (ClutterActor *actor,
events may also be emitted.</para>
<section>
<title>Creating a full scrollable actor</title>
<title>Creating a scrolling viewport for an actor</title>
<para>While the simple outline above explains the basics
of how to connect to scroll events, it doesn't do much to
help with really implementing a scrollable actor. That's what
we'll do in this section. The full code for the example we'll
walk through here is available
<link linkend="events-mouse-scroll-example">in this later
section</link>.</para>
help with <emphasis>really</emphasis> implementing scrolling
over an actor. That's what we'll do in this section.</para>
<note>
<para>The full code for the example we'll walk through here is
available in <link linkend="events-mouse-scroll-example">this later
section</link>.</para>
</note>
<para>Scrolling over an actor actually requires coordination
between two components:</para>
<orderedlist>
<listitem>
<formalpara>
<title>Scrollable actor</title>
<para>An actor which is too large to fit on the stage
or inside the area of the UI assigned to it (otherwise
there's no need to scroll over it...).</para>
</formalpara>
</listitem>
<listitem>
<formalpara>
<title>Viewport</title>
<para>This displays a cropped view of part of the scrollable
actor, revealing different parts of it as scroll events
occur.</para>
</formalpara>
</listitem>
</orderedlist>
<para>Here are the steps required to set up the two actors:</para>
<orderedlist>
<listitem>
<para>Create the scrollable actor; it should be larger
than the scrollview. This example uses a <type>ClutterTexture</type>,
but any <type>ClutterActor</type> will work:</para>
<informalexample>
<programlisting>
/* get image file path, set up stage etc. */
ClutterActor *texture;
texture = clutter_texture_new ();
clutter_texture_set_keep_aspect_ratio (CLUTTER_TEXTURE (texture),
TRUE);
/*
* set the texture's height so it's as tall as the stage
* (STAGE_HEIGHT is define'd at the top of the file);
* see <link linkend="textures-aspect-ratio">this recipe</link>
* for more about loading images into textures
*/
clutter_actor_set_request_mode (texture, CLUTTER_REQUEST_WIDTH_FOR_HEIGHT);
clutter_actor_set_height (texture, STAGE_HEIGHT);
/* load the image file */
clutter_texture_set_from_file (CLUTTER_TEXTURE (texture),
image_file_path,
NULL);
</programlisting>
</informalexample>
</listitem>
<listitem>
<para>Create the viewport. The simplest way to do
this is with a <type>ClutterGroup</type>:</para>
<informalexample>
<programlisting>
<![CDATA[
ClutterActor *viewport;
viewport = clutter_group_new ();
/* viewport is _shorter_ than the stage (and the texture) */
clutter_actor_set_size (viewport, STAGE_WIDTH, STAGE_HEIGHT * 0.5);
/* align the viewport to the center of the stage's y axis */
clutter_actor_add_constraint (viewport,
clutter_align_constraint_new (stage, CLUTTER_BIND_Y, 0.5));
/* viewport needs to respond to scroll events */
clutter_actor_set_reactive (viewport, TRUE);
/* clip all actors inside the viewport to that group's allocation */
clutter_actor_set_clip_to_allocation (viewport, TRUE);
]]>
</programlisting>
</informalexample>
<para>The key here is calling
<code>clutter_actor_set_clip_to_allocation (viewport, TRUE)</code>.
This configures the <varname>viewport</varname> group so
that any of its children are clipped: i.e. only the area of
the children which fits inside the group is visible. This
in turn requires setting an explicit size on the group,
rather than allowing it to size itself to fit its
children (the default).</para>
</listitem>
<listitem>
<para>Put the scrollable actor into the scroll view and
the scroll view into its container (in this case,
the default stage):</para>
<informalexample>
<programlisting>
clutter_container_add_actor (CLUTTER_CONTAINER (viewport), texture);
clutter_container_add_actor (CLUTTER_CONTAINER (stage), viewport);
</programlisting>
</informalexample>
</listitem>
<listitem>
<para>Create a callback handler for scroll event signals
emitted by the viewport:</para>
<informalexample>
<programlisting>
<![CDATA[
static gboolean
_scroll_event_cb (ClutterActor *viewport,
ClutterEvent *event,
gpointer user_data)
{
ClutterActor *scrollable = CLUTTER_ACTOR (user_data);
gfloat viewport_height = clutter_actor_get_height (viewport);
gfloat scrollable_height = clutter_actor_get_height (scrollable);
/* no need to scroll if the scrollable is shorter than the viewport */
if (scrollable_height < viewport_height)
return TRUE;
gfloat y = clutter_actor_get_y (scrollable);
ClutterScrollDirection direction;
direction = clutter_event_get_scroll_direction (event);
switch (direction)
{
case CLUTTER_SCROLL_UP:
y -= SCROLL_AMOUNT;
break;
case CLUTTER_SCROLL_DOWN:
y += SCROLL_AMOUNT;
break;
/* we're only interested in up and down */
case CLUTTER_SCROLL_LEFT:
case CLUTTER_SCROLL_RIGHT:
break;
}
/*
* the CLAMP macro returns a value for the first argument
* that falls within the range specified by the second and
* third arguments
*
* we allow the scrollable's y position to be decremented to the point
* where its base is aligned with the base of the viewport
*/
y = CLAMP (y,
viewport_height - scrollable_height,
0.0);
clutter_actor_animate (scrollable,
CLUTTER_EASE_OUT_CUBIC,
300,
"y", y,
NULL);
return TRUE;
}
]]>
</programlisting>
</informalexample>
<para>The approach taken here is to move the scrollable
actor up, relative to the viewport. Initially, the
scrollable will have a <code>y</code> coordinate value
of <code>0.0</code> (it is aligned to the top of the viewport).
Scrolling up subtracts from the
<code>y</code> coordinate (down to a minumum of
<code>viewport_height - scrollable_height</code>). This moves
the top of the scrollable "outside" the clip area of the
viewport; simultaneously, more of the bottom part of the
scrollable moves into the clip area, becoming visible.</para>
<para>Scrolling down adds to the <code>y</code> coordinate
(but only up to a maximum value of <code>0.0</code>).</para>
</listitem>
<listitem>
<para>Connect the callback handler to the signal; note
that we pass the scrollable (the texture) to the callback,
as we're moving the texture inside the viewport to
create the scrolling effect:</para>
<informalexample>
<programlisting>
g_signal_connect (viewport,
"scroll-event",
G_CALLBACK (_scroll_event_cb),
texture);
</programlisting>
</informalexample>
</listitem>
</orderedlist>
</section>
</section>
@ -451,7 +661,7 @@ _scroll_event_cb (ClutterActor *actor,
<title>Full example</title>
<example id="events-mouse-scroll-example">
<title>Mouse scrolling over <type>ClutterActor</type></title>
<title>Mouse scrolling over a <type>ClutterActor</type></title>
<programlisting>
<xi:include href="examples/events-mouse-scroll.c" parse="text">
<xi:fallback>a code sample should be here... but isn't</xi:fallback>

View File

@ -4,8 +4,6 @@
#define STAGE_WIDTH STAGE_HEIGHT
#define SCROLL_AMOUNT STAGE_HEIGHT * 0.125
static const ClutterColor stage_color = { 0x33, 0x33, 0x55, 0xff };
static gboolean
_scroll_event_cb (ClutterActor *viewport,
ClutterEvent *event,
@ -13,6 +11,13 @@ _scroll_event_cb (ClutterActor *viewport,
{
ClutterActor *scrollable = CLUTTER_ACTOR (user_data);
gfloat viewport_height = clutter_actor_get_height (viewport);
gfloat scrollable_height = clutter_actor_get_height (scrollable);
/* no need to scroll if the scrollable is shorter than the viewport */
if (scrollable_height < viewport_height)
return TRUE;
gfloat y = clutter_actor_get_y (scrollable);
ClutterScrollDirection direction;
@ -26,14 +31,23 @@ _scroll_event_cb (ClutterActor *viewport,
case CLUTTER_SCROLL_DOWN:
y += SCROLL_AMOUNT;
break;
/* we're only interested in up and down */
case CLUTTER_SCROLL_LEFT:
case CLUTTER_SCROLL_RIGHT:
break;
}
/*
* the CLAMP macro returns a value for the first argument
* that falls within the range specified by the second and
* third arguments
*
* we allow the scrollable's y position to be decremented to the point
* where its base is aligned with the base of the viewport
*/
y = CLAMP (y,
clutter_actor_get_height (viewport)
- clutter_actor_get_height (scrollable),
viewport_height - scrollable_height,
0.0);
clutter_actor_animate (scrollable,
@ -48,6 +62,13 @@ _scroll_event_cb (ClutterActor *viewport,
int
main (int argc, char *argv[])
{
gchar *image_file_path = TESTS_DATA_DIR "/redhand.png";
if (argc > 1)
{
image_file_path = argv[1];
}
ClutterActor *stage;
ClutterActor *viewport;
ClutterActor *texture;
@ -56,8 +77,6 @@ main (int argc, char *argv[])
stage = clutter_stage_get_default ();
clutter_actor_set_size (stage, STAGE_WIDTH, STAGE_HEIGHT);
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
/* the scrollable actor */
texture = clutter_texture_new ();
@ -69,7 +88,7 @@ main (int argc, char *argv[])
clutter_actor_set_height (texture, STAGE_HEIGHT);
clutter_texture_set_from_file (CLUTTER_TEXTURE (texture),
TESTS_DATA_DIR "/redhand.png",
image_file_path,
NULL);
/* the viewport which the box is scrolled within */