1/* GTK - The GIMP Toolkit
2 * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "config.h"
19
20#include "gtkrender.h"
21
22#include <math.h>
23
24#include "gtkcsscornervalueprivate.h"
25#include "gtkcssimagevalueprivate.h"
26#include "gtkcssnumbervalueprivate.h"
27#include "gtkcsscolorvalueprivate.h"
28#include "gtkcssshadowvalueprivate.h"
29#include "gtkcsstransformvalueprivate.h"
30#include "gtkrendericonprivate.h"
31#include "gtkstylecontextprivate.h"
32
33#include "gsk/gskroundedrectprivate.h"
34#include <gdk/gdktextureprivate.h>
35
36static void
37gtk_do_render_icon (GtkStyleContext *context,
38 cairo_t *cr,
39 double x,
40 double y,
41 double width,
42 double height)
43{
44 GtkSnapshot *snapshot;
45 GskRenderNode *node;
46
47 snapshot = gtk_snapshot_new ();
48 gtk_css_style_snapshot_icon (style: gtk_style_context_lookup_style (context), snapshot, width, height);
49 node = gtk_snapshot_free_to_node (snapshot);
50 if (node == NULL)
51 return;
52
53 cairo_save (cr);
54 cairo_translate (cr, tx: x, ty: y);
55 gsk_render_node_draw (node, cr);
56 cairo_restore (cr);
57
58 gsk_render_node_unref (node);
59}
60
61/**
62 * gtk_render_check:
63 * @context: a `GtkStyleContext`
64 * @cr: a `cairo_t`
65 * @x: X origin of the rectangle
66 * @y: Y origin of the rectangle
67 * @width: rectangle width
68 * @height: rectangle height
69 *
70 * Renders a checkmark (as in a `GtkCheckButton`).
71 *
72 * The %GTK_STATE_FLAG_CHECKED state determines whether the check is
73 * on or off, and %GTK_STATE_FLAG_INCONSISTENT determines whether it
74 * should be marked as undefined.
75 *
76 * Typical checkmark rendering:
77 *
78 * ![](checks.png)
79 **/
80void
81gtk_render_check (GtkStyleContext *context,
82 cairo_t *cr,
83 double x,
84 double y,
85 double width,
86 double height)
87{
88 g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
89 g_return_if_fail (cr != NULL);
90
91 if (width <= 0 || height <= 0)
92 return;
93
94 gtk_do_render_icon (context, cr, x, y, width, height);
95}
96
97/**
98 * gtk_render_option:
99 * @context: a `GtkStyleContext`
100 * @cr: a `cairo_t`
101 * @x: X origin of the rectangle
102 * @y: Y origin of the rectangle
103 * @width: rectangle width
104 * @height: rectangle height
105 *
106 * Renders an option mark (as in a radio button), the %GTK_STATE_FLAG_CHECKED
107 * state will determine whether the option is on or off, and
108 * %GTK_STATE_FLAG_INCONSISTENT whether it should be marked as undefined.
109 *
110 * Typical option mark rendering:
111 *
112 * ![](options.png)
113 **/
114void
115gtk_render_option (GtkStyleContext *context,
116 cairo_t *cr,
117 double x,
118 double y,
119 double width,
120 double height)
121{
122 g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
123 g_return_if_fail (cr != NULL);
124
125 if (width <= 0 || height <= 0)
126 return;
127
128 gtk_do_render_icon (context, cr, x, y, width, height);
129}
130
131/**
132 * gtk_render_arrow:
133 * @context: a `GtkStyleContext`
134 * @cr: a `cairo_t`
135 * @angle: arrow angle from 0 to 2 * %G_PI, being 0 the arrow pointing to the north
136 * @x: X origin of the render area
137 * @y: Y origin of the render area
138 * @size: square side for render area
139 *
140 * Renders an arrow pointing to @angle.
141 *
142 * Typical arrow rendering at 0, 1⁄2 π;, π; and 3⁄2 π:
143 *
144 * ![](arrows.png)
145 **/
146void
147gtk_render_arrow (GtkStyleContext *context,
148 cairo_t *cr,
149 double angle,
150 double x,
151 double y,
152 double size)
153{
154 g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
155 g_return_if_fail (cr != NULL);
156
157 if (size <= 0)
158 return;
159
160 gtk_do_render_icon (context, cr, x, y, width: size, height: size);
161}
162
163/**
164 * gtk_render_background:
165 * @context: a `GtkStyleContext`
166 * @cr: a `cairo_t`
167 * @x: X origin of the rectangle
168 * @y: Y origin of the rectangle
169 * @width: rectangle width
170 * @height: rectangle height
171 *
172 * Renders the background of an element.
173 *
174 * Typical background rendering, showing the effect of
175 * `background-image`, `border-width` and `border-radius`:
176 *
177 * ![](background.png)
178 **/
179void
180gtk_render_background (GtkStyleContext *context,
181 cairo_t *cr,
182 double x,
183 double y,
184 double width,
185 double height)
186{
187 GtkSnapshot *snapshot;
188 GskRenderNode *node;
189
190 g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
191 g_return_if_fail (cr != NULL);
192
193 if (width <= 0 || height <= 0)
194 return;
195
196 snapshot = gtk_snapshot_new ();
197 gtk_snapshot_render_background (snapshot, context, x, y, width, height);
198 node = gtk_snapshot_free_to_node (snapshot);
199 if (node == NULL)
200 return;
201
202 cairo_save (cr);
203 gsk_render_node_draw (node, cr);
204 cairo_restore (cr);
205
206 gsk_render_node_unref (node);
207}
208
209/**
210 * gtk_render_frame:
211 * @context: a `GtkStyleContext`
212 * @cr: a `cairo_t`
213 * @x: X origin of the rectangle
214 * @y: Y origin of the rectangle
215 * @width: rectangle width
216 * @height: rectangle height
217 *
218 * Renders a frame around the rectangle defined by @x, @y, @width, @height.
219 *
220 * Examples of frame rendering, showing the effect of `border-image`,
221 * `border-color`, `border-width`, `border-radius` and junctions:
222 *
223 * ![](frames.png)
224 **/
225void
226gtk_render_frame (GtkStyleContext *context,
227 cairo_t *cr,
228 double x,
229 double y,
230 double width,
231 double height)
232{
233 GtkSnapshot *snapshot;
234 GskRenderNode *node;
235
236 g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
237 g_return_if_fail (cr != NULL);
238
239 if (width <= 0 || height <= 0)
240 return;
241
242 snapshot = gtk_snapshot_new ();
243 gtk_snapshot_render_frame (snapshot, context, x, y, width, height);
244 node = gtk_snapshot_free_to_node (snapshot);
245 if (node == NULL)
246 return;
247
248 cairo_save (cr);
249 gsk_render_node_draw (node, cr);
250 cairo_restore (cr);
251
252 gsk_render_node_unref (node);
253}
254
255/**
256 * gtk_render_expander:
257 * @context: a `GtkStyleContext`
258 * @cr: a `cairo_t`
259 * @x: X origin of the rectangle
260 * @y: Y origin of the rectangle
261 * @width: rectangle width
262 * @height: rectangle height
263 *
264 * Renders an expander (as used in `GtkTreeView` and `GtkExpander`) in the area
265 * defined by @x, @y, @width, @height. The state %GTK_STATE_FLAG_CHECKED
266 * determines whether the expander is collapsed or expanded.
267 *
268 * Typical expander rendering:
269 *
270 * ![](expanders.png)
271 **/
272void
273gtk_render_expander (GtkStyleContext *context,
274 cairo_t *cr,
275 double x,
276 double y,
277 double width,
278 double height)
279{
280 g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
281 g_return_if_fail (cr != NULL);
282
283 if (width <= 0 || height <= 0)
284 return;
285
286 gtk_do_render_icon (context, cr, x, y, width, height);
287}
288
289/**
290 * gtk_render_focus:
291 * @context: a `GtkStyleContext`
292 * @cr: a `cairo_t`
293 * @x: X origin of the rectangle
294 * @y: Y origin of the rectangle
295 * @width: rectangle width
296 * @height: rectangle height
297 *
298 * Renders a focus indicator on the rectangle determined by @x, @y, @width, @height.
299 *
300 * Typical focus rendering:
301 *
302 * ![](focus.png)
303 **/
304void
305gtk_render_focus (GtkStyleContext *context,
306 cairo_t *cr,
307 double x,
308 double y,
309 double width,
310 double height)
311{
312 GtkSnapshot *snapshot;
313 GskRenderNode *node;
314
315 g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
316 g_return_if_fail (cr != NULL);
317
318 if (width <= 0 || height <= 0)
319 return;
320
321 snapshot = gtk_snapshot_new ();
322 gtk_snapshot_render_frame (snapshot, context, x, y, width, height);
323 node = gtk_snapshot_free_to_node (snapshot);
324 if (node == NULL)
325 return;
326
327 cairo_save (cr);
328 gsk_render_node_draw (node, cr);
329 cairo_restore (cr);
330
331 gsk_render_node_unref (node);
332}
333
334/**
335 * gtk_render_layout:
336 * @context: a `GtkStyleContext`
337 * @cr: a `cairo_t`
338 * @x: X origin
339 * @y: Y origin
340 * @layout: the `PangoLayout` to render
341 *
342 * Renders @layout on the coordinates @x, @y
343 **/
344void
345gtk_render_layout (GtkStyleContext *context,
346 cairo_t *cr,
347 double x,
348 double y,
349 PangoLayout *layout)
350{
351 GtkSnapshot *snapshot;
352 GskRenderNode *node;
353
354 g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
355 g_return_if_fail (PANGO_IS_LAYOUT (layout));
356 g_return_if_fail (cr != NULL);
357
358 snapshot = gtk_snapshot_new ();
359 gtk_snapshot_render_layout (snapshot, context, x, y, layout);
360 node = gtk_snapshot_free_to_node (snapshot);
361 if (node == NULL)
362 return;
363
364 cairo_save (cr);
365 gsk_render_node_draw (node, cr);
366 cairo_restore (cr);
367
368 gsk_render_node_unref (node);
369}
370
371/**
372 * gtk_render_line:
373 * @context: a `GtkStyleContext`
374 * @cr: a `cairo_t`
375 * @x0: X coordinate for the origin of the line
376 * @y0: Y coordinate for the origin of the line
377 * @x1: X coordinate for the end of the line
378 * @y1: Y coordinate for the end of the line
379 *
380 * Renders a line from (x0, y0) to (x1, y1).
381 **/
382void
383gtk_render_line (GtkStyleContext *context,
384 cairo_t *cr,
385 double x0,
386 double y0,
387 double x1,
388 double y1)
389{
390 const GdkRGBA *color;
391
392 g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
393 g_return_if_fail (cr != NULL);
394
395 cairo_save (cr);
396
397 color = gtk_css_color_value_get_rgba (color: _gtk_style_context_peek_property (context, property_id: GTK_CSS_PROPERTY_COLOR));
398
399 cairo_set_line_cap (cr, line_cap: CAIRO_LINE_CAP_SQUARE);
400 cairo_set_line_width (cr, width: 1);
401
402 cairo_move_to (cr, x: x0 + 0.5, y: y0 + 0.5);
403 cairo_line_to (cr, x: x1 + 0.5, y: y1 + 0.5);
404
405 gdk_cairo_set_source_rgba (cr, rgba: color);
406 cairo_stroke (cr);
407
408 cairo_restore (cr);
409}
410
411/**
412 * gtk_render_handle:
413 * @context: a `GtkStyleContext`
414 * @cr: a `cairo_t`
415 * @x: X origin of the rectangle
416 * @y: Y origin of the rectangle
417 * @width: rectangle width
418 * @height: rectangle height
419 *
420 * Renders a handle (as in `GtkPaned` and `GtkWindow`’s resize grip),
421 * in the rectangle determined by @x, @y, @width, @height.
422 *
423 * Handles rendered for the paned and grip classes:
424 *
425 * ![](handles.png)
426 **/
427void
428gtk_render_handle (GtkStyleContext *context,
429 cairo_t *cr,
430 double x,
431 double y,
432 double width,
433 double height)
434{
435 g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
436 g_return_if_fail (cr != NULL);
437
438 if (width <= 0 || height <= 0)
439 return;
440
441 gtk_render_background (context, cr, x, y, width, height);
442 gtk_render_frame (context, cr, x, y, width, height);
443
444 gtk_do_render_icon (context, cr, x, y, width, height);
445}
446
447/**
448 * gtk_render_activity:
449 * @context: a `GtkStyleContext`
450 * @cr: a `cairo_t`
451 * @x: X origin of the rectangle
452 * @y: Y origin of the rectangle
453 * @width: rectangle width
454 * @height: rectangle height
455 *
456 * Renders an activity indicator (such as in `GtkSpinner`).
457 * The state %GTK_STATE_FLAG_CHECKED determines whether there is
458 * activity going on.
459 **/
460void
461gtk_render_activity (GtkStyleContext *context,
462 cairo_t *cr,
463 double x,
464 double y,
465 double width,
466 double height)
467{
468 g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
469 g_return_if_fail (cr != NULL);
470
471 if (width <= 0 || height <= 0)
472 return;
473
474 gtk_do_render_icon (context, cr, x, y, width, height);
475}
476
477/**
478 * gtk_render_icon:
479 * @context: a `GtkStyleContext`
480 * @cr: a `cairo_t`
481 * @texture: a `GdkTexture` containing the icon to draw
482 * @x: X position for the @texture
483 * @y: Y position for the @texture
484 *
485 * Renders the icon in @texture at the specified @x and @y coordinates.
486 *
487 * This function will render the icon in @texture at exactly its size,
488 * regardless of scaling factors, which may not be appropriate when
489 * drawing on displays with high pixel densities.
490 *
491 **/
492void
493gtk_render_icon (GtkStyleContext *context,
494 cairo_t *cr,
495 GdkTexture *texture,
496 double x,
497 double y)
498{
499 GtkSnapshot *snapshot;
500 GskRenderNode *node;
501
502 snapshot = gtk_snapshot_new ();
503 gtk_css_style_snapshot_icon_paintable (style: gtk_style_context_lookup_style (context),
504 snapshot,
505 paintable: GDK_PAINTABLE (ptr: texture),
506 width: gdk_texture_get_width (texture),
507 height: gdk_texture_get_height (texture));
508 node = gtk_snapshot_free_to_node (snapshot);
509 if (node == NULL)
510 return;
511
512 cairo_save (cr);
513 cairo_translate (cr, tx: x, ty: y);
514 gsk_render_node_draw (node, cr);
515 cairo_restore (cr);
516}
517

source code of gtk/gtk/gtkrender.c