mirror of
https://github.com/brl/mutter.git
synced 2025-01-23 18:09:10 +00:00
Merge remote branch 'elliot/cookbook-events-mouse-scroll'
* elliot/cookbook-events-mouse-scroll: cookbook: Cleaning up grammar and wording in mouse scroll recipe cookbook: Added more explanation about setting y coord on scrollable cookbook: Mentioned the animation in the sample code cookbook: Included video of the scroll example running cookbook: Made stage slightly smaller for scroll event example cookbook: Added video showing scrollable actor cookbook: Added walk through of code example for mouse scroll cookbook: Fixed link to example in mouse scroll recipe cookbook: Simplified full scroll example cookbook: Improved wording and formatting in mouse scroll intro. cookbook: Handle all possible mouse scroll directions cookbook: Build mouse scroll example with cookbook cookbook: Cleaned up redundant comments in code example cookbook: Added xmlns for XInclude to events docbook file cookbook: Added basic mouse scroll recipe
This commit is contained in:
commit
6c6e93d27a
@ -52,6 +52,7 @@ VIDEO_FILES = \
|
||||
videos/animations-rotating-z-centered.ogv \
|
||||
videos/animations-rotating-container-reverses-direction.ogv \
|
||||
videos/textures-split-go.ogv \
|
||||
videos/events-mouse-scroll.ogv \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = \
|
||||
|
@ -1,7 +1,7 @@
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
|
||||
<chapter id="events">
|
||||
<chapter id="events" xmlns:xi="http://www.w3.org/2003/XInclude">
|
||||
<title>Events</title>
|
||||
|
||||
<epigraph>
|
||||
@ -345,4 +345,357 @@ clutter_stage_set_key_focus (stage, actor);
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
<section id="events-mouse-scroll">
|
||||
<title>Detecting mouse scrolling on an actor</title>
|
||||
|
||||
<section>
|
||||
<title>Problem</title>
|
||||
|
||||
<para>You want to detect when the mouse is scrolled on an
|
||||
actor (e.g. the pointer is over an actor when a mouse
|
||||
wheel is scrolled).</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Solution</title>
|
||||
|
||||
<para>Connect a callback handler to the <code>scroll-event</code>
|
||||
signal of an actor.</para>
|
||||
|
||||
<para>First, ensure that the actor is reactive (i.e. will
|
||||
respond to events):</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
clutter_actor_set_reactive (actor, TRUE);
|
||||
]]>
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>Next, create a callback handler to examine the scroll
|
||||
event and respond to it:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
static gboolean
|
||||
_scroll_event_cb (ClutterActor *actor,
|
||||
ClutterEvent *event,
|
||||
gpointer user_data)
|
||||
{
|
||||
/* determine the direction the mouse was scrolled */
|
||||
ClutterScrollDirection direction;
|
||||
direction = clutter_event_get_scroll_direction (event);
|
||||
|
||||
/* replace these stubs with real code to move the actor etc. */
|
||||
switch (direction)
|
||||
{
|
||||
case CLUTTER_SCROLL_UP:
|
||||
g_debug ("Scrolled up");
|
||||
break;
|
||||
case CLUTTER_SCROLL_DOWN:
|
||||
g_debug ("Scrolled down");
|
||||
break;
|
||||
case CLUTTER_SCROLL_RIGHT:
|
||||
g_debug ("Scrolled right");
|
||||
break;
|
||||
case CLUTTER_SCROLL_LEFT:
|
||||
g_debug ("Scrolled left");
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE; /* event has been handled */
|
||||
}
|
||||
]]>
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
<para>Finally, connect the callback handler to the
|
||||
<code>scroll-event</code> signal of the actor:</para>
|
||||
|
||||
<informalexample>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
g_signal_connect (actor,
|
||||
"scroll-event",
|
||||
G_CALLBACK (_scroll_event_cb),
|
||||
NULL);
|
||||
]]>
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Discussion</title>
|
||||
|
||||
<para>A standard mouse wheel will only return up and
|
||||
down movements; but in cases where the mouse has left and
|
||||
right scrolling (e.g. a trackball mouse or trackpad), left and
|
||||
right scroll events may also be emitted.</para>
|
||||
|
||||
<section>
|
||||
<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 <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 viewport. 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)
|
||||
*/
|
||||
clutter_actor_set_request_mode (texture, CLUTTER_REQUEST_WIDTH_FOR_HEIGHT);
|
||||
clutter_actor_set_height (texture, STAGE_HEIGHT);
|
||||
|
||||
/*
|
||||
* load the image file;
|
||||
* see <link linkend="textures-aspect-ratio">this recipe</link> for more about loading images into textures
|
||||
*/
|
||||
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 parts of
|
||||
its children which fit inside its allocation are visible. This
|
||||
in turn requires setting an explicit size on the group,
|
||||
rather than allowing it to size itself to fit its
|
||||
children (the latter is the default).</para>
|
||||
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Put the scrollable actor into the viewport; and
|
||||
the viewport 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 <code>scroll-event</code>
|
||||
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);
|
||||
|
||||
/* animate the change to the scrollable's y coordinate */
|
||||
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> (aligned to the top of the viewport).
|
||||
Scrolling up decrements the
|
||||
<code>y</code> coordinate (down to a minumum of
|
||||
<code>viewport_height - scrollable_height</code>). This moves
|
||||
the top of the scrollable actor "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 increments the <code>y</code> coordinate
|
||||
(but only up to a maximum value of <code>0.0</code>).</para>
|
||||
|
||||
<para>To see how this works in practice, look at
|
||||
<link linkend="events-mouse-scroll-example">the code
|
||||
sample</link>. There, the height of the scrollable actor is
|
||||
set to <code>300</code> and the height of the viewport to
|
||||
<code>150</code>. This means that the <code>y</code>
|
||||
coordinate value for the scrollable actor will vary between
|
||||
<code>-150.0</code>: <code>150</code> (the viewport's height)
|
||||
<code>- 300</code> (the scrollable actor's height), making
|
||||
its base visible and clipping its top; and
|
||||
<code>0.0</code>, where its top is visible and its base
|
||||
clipped.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Connect the callback handler to the signal; note
|
||||
that we pass the scrollable actor (the texture) to the callback,
|
||||
as we're moving the texture relative to 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>
|
||||
|
||||
<para>Here's a video of the result:</para>
|
||||
|
||||
<inlinemediaobject>
|
||||
<videoobject>
|
||||
<videodata fileref="videos/events-mouse-scroll.ogv"/>
|
||||
</videoobject>
|
||||
<alt>
|
||||
<para>Video showing a scrollable actor</para>
|
||||
</alt>
|
||||
</inlinemediaobject>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Full example</title>
|
||||
|
||||
<example id="events-mouse-scroll-example">
|
||||
<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>
|
||||
</xi:include>
|
||||
</programlisting>
|
||||
</example>
|
||||
</section>
|
||||
|
||||
</section>
|
||||
</chapter>
|
||||
|
1
doc/cookbook/examples/.gitignore
vendored
1
doc/cookbook/examples/.gitignore
vendored
@ -5,3 +5,4 @@
|
||||
/textures-sub-texture
|
||||
/layouts-stacking
|
||||
/layouts-stacking-diff-sized-actors
|
||||
/events-mouse-scroll
|
||||
|
@ -10,6 +10,7 @@ noinst_PROGRAMS = \
|
||||
textures-sub-texture \
|
||||
layouts-stacking \
|
||||
layouts-stacking-diff-sized-actors \
|
||||
events-mouse-scroll \
|
||||
$(NULL)
|
||||
|
||||
INCLUDES = \
|
||||
@ -38,3 +39,4 @@ textures_split_go_SOURCES = textures-split-go.c
|
||||
textures_sub_texture_SOURCES = textures-sub-texture.c
|
||||
layouts_stacking_SOURCES = layouts-stacking.c
|
||||
layouts_stacking_diff_sized_actors_SOURCES = layouts-stacking-diff-sized-actors.c
|
||||
events_mouse_scroll_SOURCES = events-mouse-scroll.c
|
||||
|
126
doc/cookbook/examples/events-mouse-scroll.c
Normal file
126
doc/cookbook/examples/events-mouse-scroll.c
Normal file
@ -0,0 +1,126 @@
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
#define STAGE_HEIGHT 300
|
||||
#define STAGE_WIDTH STAGE_HEIGHT
|
||||
#define SCROLL_AMOUNT STAGE_HEIGHT * 0.125
|
||||
|
||||
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);
|
||||
|
||||
/* animate the change to the scrollable's y coordinate */
|
||||
clutter_actor_animate (scrollable,
|
||||
CLUTTER_EASE_OUT_CUBIC,
|
||||
300,
|
||||
"y", y,
|
||||
NULL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
clutter_init (&argc, &argv);
|
||||
|
||||
stage = clutter_stage_get_default ();
|
||||
clutter_actor_set_size (stage, STAGE_WIDTH, STAGE_HEIGHT);
|
||||
|
||||
/* the scrollable actor */
|
||||
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 */
|
||||
clutter_actor_set_request_mode (texture, CLUTTER_REQUEST_WIDTH_FOR_HEIGHT);
|
||||
clutter_actor_set_height (texture, STAGE_HEIGHT);
|
||||
|
||||
clutter_texture_set_from_file (CLUTTER_TEXTURE (texture),
|
||||
image_file_path,
|
||||
NULL);
|
||||
|
||||
/* the viewport which the box is scrolled within */
|
||||
viewport = clutter_group_new ();
|
||||
|
||||
/* viewport is shorter than the stage */
|
||||
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);
|
||||
|
||||
/* put the texture inside the viewport */
|
||||
clutter_container_add_actor (CLUTTER_CONTAINER (viewport), texture);
|
||||
|
||||
/* add the viewport to the stage */
|
||||
clutter_container_add_actor (CLUTTER_CONTAINER (stage), viewport);
|
||||
|
||||
g_signal_connect (viewport,
|
||||
"scroll-event",
|
||||
G_CALLBACK (_scroll_event_cb),
|
||||
texture);
|
||||
|
||||
clutter_actor_show (stage);
|
||||
|
||||
clutter_main ();
|
||||
|
||||
return 0;
|
||||
}
|
BIN
doc/cookbook/videos/events-mouse-scroll.ogv
Normal file
BIN
doc/cookbook/videos/events-mouse-scroll.ogv
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user