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 | |
36 | static void |
37 | gtk_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 | **/ |
80 | void |
81 | gtk_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 | **/ |
114 | void |
115 | gtk_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 | **/ |
146 | void |
147 | gtk_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 | **/ |
179 | void |
180 | gtk_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 | **/ |
225 | void |
226 | gtk_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 | **/ |
272 | void |
273 | gtk_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 | **/ |
304 | void |
305 | gtk_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 | **/ |
344 | void |
345 | gtk_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 | **/ |
382 | void |
383 | gtk_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 | **/ |
427 | void |
428 | gtk_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 | **/ |
460 | void |
461 | gtk_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 | **/ |
492 | void |
493 | gtk_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 | |