/usr/share/gtk-doc/html/clutter-cookbook/actors-non-rectangular.html is in libclutter-1.0-doc 1.20.0-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 | <html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>6. Creating an actor with a non-rectangular shape</title><link rel="stylesheet" type="text/css" href="style.css"><meta name="generator" content="DocBook XSL Stylesheets V1.78.1"><link rel="home" href="index.html" title="The Clutter Cookbook"><link rel="up" href="actors.html" title="Chapter 2. Actors"><link rel="prev" href="actors-opacity.html" title="5. Making an actor transparent by changing its opacity"><link rel="next" href="events.html" title="Chapter 3. Events"></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">6. Creating an actor with a non-rectangular shape</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="actors-opacity.html">Prev</a> </td><th width="60%" align="center">Chapter 2. Actors</th><td width="20%" align="right"> <a accesskey="n" href="events.html">Next</a></td></tr></table><hr></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="actors-non-rectangular"></a>6. Creating an actor with a non-rectangular shape</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp70690464"></a>6.1. Problem</h3></div></div></div><p>You want to create a <span class="type">ClutterActor</span> subclass,
but don't want it to be rectangular; for example, you want a
star-shaped actor.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp70692336"></a>6.2. Solution</h3></div></div></div><p>Use Cogl primitives to draw the actor.</p><p>Below is an example of the pick and paint implementations for a
star-shaped <span class="type">StarActor</span> class (an extension of
<span class="type">ClutterActor</span>).</p><p>Like <span class="type">ClutterRectangle</span>, it has a private
struct internally, which contains a <span class="type">ClutterColor</span>
denoting the color it should be painted. This is used to set the Cogl
source color.</p><div class="informalexample"><pre class="programlisting">static void
star_actor_paint (ClutterActor *actor)
{
ClutterActorBox allocation = { 0, };
gfloat width, height;
guint tmp_alpha;
/* priv is a private internal struct */
ClutterColor color = STAR_ACTOR (actor)->priv->color;
clutter_actor_get_allocation_box (actor, &allocation);
clutter_actor_box_get_size (&allocation, &width, &height);
tmp_alpha = clutter_actor_get_paint_opacity (actor)
* color.alpha
/ 255;
cogl_path_new ();
cogl_set_source_color4ub (color.red,
color.green,
color.blue,
tmp_alpha);
/* create and store a path describing a star */
cogl_path_move_to (width * 0.5, 0);
cogl_path_line_to (width, height * 0.75);
cogl_path_line_to (0, height * 0.75);
cogl_path_move_to (width * 0.5, height);
cogl_path_line_to (0, height * 0.25);
cogl_path_line_to (width, height * 0.25);
cogl_path_line_to (width * 0.5, height);
cogl_path_fill ();
}
static void
star_actor_pick (ClutterActor *actor,
const ClutterColor *pick_color)
{
if (!clutter_actor_should_pick_paint (actor))
return;
ClutterActorBox allocation = { 0, };
gfloat width, height;
clutter_actor_get_allocation_box (actor, &allocation);
clutter_actor_box_get_size (&allocation, &width, &height);
cogl_path_new ();
cogl_set_source_color4ub (pick_color->red,
pick_color->green,
pick_color->blue,
pick_color->alpha);
/* create and store a path describing a star */
cogl_path_move_to (width * 0.5, 0);
cogl_path_line_to (width, height * 0.75);
cogl_path_line_to (0, height * 0.75);
cogl_path_move_to (width * 0.5, height);
cogl_path_line_to (0, height * 0.25);
cogl_path_line_to (width, height * 0.25);
cogl_path_line_to (width * 0.5, height);
cogl_path_fill ();
}</pre></div><p>If you need more information about how to implement your own
<span class="type">ClutterActor</span>, see the Clutter reference
manual.</p><p>Note that the code in these two functions is virtually identical:
the Discussion section suggests how to remove this redundancy.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="idp70701056"></a>6.3. Discussion</h3></div></div></div><p>The above is one approach to creating a non-rectangular
actor. But it's also possible to get a similar effect by
subclassing an existing actor (like <span class="type">ClutterRectangle</span>)
and giving it a non-rectangular appearance. You could do this by
making the underlying rectangle transparent and then drawing on
top of it (e.g. using Cairo or Cogl).</p><p>However, if you then made such an actor reactive, events
like mouse button presses would be triggered from anywhere on
the underlying rectangle. This is true even if the visible part
of the actor only partially fills the rectangle (underneath, it's
still a rectangle).</p><p>The advantage of using Cogl paths is that the reactive area
of the actor is defined by the Cogl path. So if you have a
star-shaped actor, only clicks (or other events) directly on the
star will have any effect on it.</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="idp70704400"></a>6.3.1. Cogl path coordinates</h4></div></div></div><p>In the example shown, <code class="function">cogl_path_move_to()</code>
and <code class="function">cogl_path_line_to()</code> are used. These
take absolute <code class="code">x</code> and <code class="code">y</code> coordinates as
arguments, relative to the GL 'modelview' transform matrix; in
the case of an actor's <code class="function">paint</code> implementation,
relative to the bounding box for the actor. So if an actor has
width and height of 50 pixels, and you used
<code class="function">cogl_move_to (25, 25)</code> in its
<code class="function">paint</code> implementation, the "pen"
moves to the centre of the actor, regardless of where the actor
is positioned on the stage. Similarly, using
<code class="function">cogl_path_line_to()</code> creates a line segment
from the current pen position to the absolute coordinates
(<code class="code">x</code>, <code class="code">y</code>) specified.</p><p>The Cogl API also provides various "rel" variants of the path
functions (e.g. <code class="function">cogl_path_rel_line_to()</code>), which
create path segments relative to the current pen position (i.e.
<code class="code">pen_x + x</code>, <code class="code">pen_y + y</code>).</p><p>It's important to note that the path isn't drawn until you
call <code class="function">cogl_path_stroke()</code> (to draw the path segments)
or <code class="function">cogl_path_fill()</code> (to fill the area enclosed by
the path). The path is cleared once it's been drawn.
Using the <code class="function">*_preserve</code> variants of these functions draws
the path and retains it (so it could be drawn again).</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="idp70717152"></a>6.3.2. Other Cogl primitives</h4></div></div></div><p>Note that the Cogl primitives API provides other types of path
segment beyond straight lines that we didn't use here, including:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>Bezier curves (<code class="function">cogl_path_curve_to()</code>)</p></li><li class="listitem"><p>Arcs (<code class="function">cogl_path_arc()</code>)</p></li><li class="listitem"><p>Polygons (<code class="function">cogl_path_polygon()</code>)</p></li><li class="listitem"><p>Rectangles (<code class="function">cogl_path_rectangle()</code>)</p></li><li class="listitem"><p>Rectangles with rounded corners
(<code class="function">cogl_path_round_rectangle()</code>)</p></li><li class="listitem"><p>Ellipses (<code class="function">cogl_path_ellipse()</code>)</p></li></ul></div><p>One important drawback of using Cogl path primitives is that
they will not produce high quality results; more specifically,
Cogl does not draw anti-aliased primitives. It is recommended to use
the Cairo API to draw during the paint sequence, and the Cogl API
to draw during the pick sequence.</p><p>If you need more flexibility than is available in the Cogl path
API, you can make direct use of the <span class="type">CoglVertexBuffer</span>
API instead. This is a lower-level API, but could potentially
be used to draw more complex shapes.</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="idp70730592"></a>6.3.3. Using <span class="type">ClutterPath</span> to store the path</h4></div></div></div><p>The disadvantage of the code above is that the paths are stored in two
places: once for <code class="function">pick</code>, and once for
<code class="function">paint</code>. It would make sense to store the
path in one place and reference it from both of these functions to
prevent duplication.</p><p>Clutter provides a <span class="type">ClutterPath</span> API for storing
generic path descriptions. It can be used to describe paths
which translate to Cogl or Cairo paths, and can also be used to
describe animation paths.</p><p>We can use a <span class="type">ClutterPath</span> instance stored
inside the actor to define the path for <code class="function">pick</code> and
<code class="function">paint</code>; then, inside those functions, we
translate the <span class="type">ClutterPath</span> into Cogl path function calls
(NB <span class="type">ClutterPath</span> is effectively a declarative method
for defining a path, while the Cogl path API is imperative).</p><p>First we add a <code class="varname">path</code> member to the private
struct for the <span class="type">StarActor</span> class (using standard
GObject mechanisms). The <code class="function">init</code> implementation for
<span class="type">StarActor</span> creates an empty path:</p><div class="informalexample"><pre class="programlisting">static void
star_actor_init (StarActor *self)
{
self->priv = STAR_ACTOR_GET_PRIVATE (self);
self->priv->path = clutter_path_new ();
clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE);
}</pre></div><p>One consideration is that the path coordinates need to
fit inside the actor's bounding box. So as the actor's allocation
changes, <code class="varname">path</code> also needs to change. We can do this
by implementing <code class="function">allocate</code> for the
<span class="type">StarActor</span> class:</p><div class="informalexample"><pre class="programlisting">static void
star_actor_allocate (ClutterActor *actor,
const ClutterActorBox *box,
ClutterAllocationFlags flags)
{
ClutterPath *path = STAR_ACTOR (actor)->priv->path;
gfloat width, height;
clutter_actor_box_get_size (box, &width, &height);
/* create and store a path describing a star */
clutter_path_clear (path);
clutter_path_add_move_to (path, width * 0.5, 0);
clutter_path_add_line_to (path, width, height * 0.75);
clutter_path_add_line_to (path, 0, height * 0.75);
clutter_path_add_move_to (path, width * 0.5, height);
clutter_path_add_line_to (path, 0, height * 0.25);
clutter_path_add_line_to (path, width, height * 0.25);
clutter_path_add_line_to (path, width * 0.5, height);
CLUTTER_ACTOR_CLASS (star_actor_parent_class)->allocate (actor, box, flags);
}</pre></div><p>This clears then adds segments to the
<span class="type">ClutterPath</span> stored with the
<span class="type">StarActor</span> instance. The positioning and
lengths of the segments are relative to the size of the actor when
its allocation changes.</p><p>The <code class="function">pick</code> and <code class="function">paint</code>
functions now reference the <span class="type">ClutterPath</span> (only the
<code class="function">pick</code> is shown below); and
to turn the path into drawing operations, we implement a
<code class="function">star_actor_convert_clutter_path_node()</code> function
which takes a <span class="type">ClutterPathNode</span> and converts it
into its Cogl equivalent:</p><div class="informalexample"><pre class="programlisting">static void
star_actor_convert_clutter_path_node (const ClutterPathNode *node,
gpointer data)
{
g_return_if_fail (node != NULL);
ClutterKnot knot;
switch (node->type)
{
case CLUTTER_PATH_MOVE_TO:
knot = node->points[0];
cogl_path_move_to (knot.x, knot.y);
break;
case CLUTTER_PATH_LINE_TO:
knot = node->points[0];
cogl_path_line_to (knot.x, knot.y);
break;
default:
break;
}
}
static void
star_actor_pick (ClutterActor *actor,
const ClutterColor *pick_color)
{
if (!clutter_actor_should_pick_paint (actor))
return;
ClutterActorBox allocation = { 0, };
gfloat width, height;
ClutterPath *path = STAR_ACTOR (actor)->priv->path;
clutter_actor_get_allocation_box (actor, &allocation);
clutter_actor_box_get_size (&allocation, &width, &height);
cogl_path_new ();
cogl_set_source_color4ub (pick_color->red,
pick_color->green,
pick_color->blue,
pick_color->alpha);
clutter_path_foreach (path, star_actor_convert_clutter_path_node, NULL);
cogl_path_fill ();
}</pre></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>The conversion function only covers
<span class="type">ClutterPathNode</span> types encountered in this
actor.</p></div><p>Instead of converting to Cogl path operations, another alternative
would be to use the <code class="function">clutter_path_to_cairo_path()</code>
function to write directly from the <span class="type">ClutterPath</span>
onto a Cairo context.</p></div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="actors-opacity.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="actors.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="events.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">5. Making an actor transparent by changing its opacity </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 3. Events</td></tr></table></div></body></html>
|