1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2011 Benjamin Otte <otte@gnome.org>
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.
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.
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/>.
20 #include "gtkroundedboxprivate.h"
22 #include "gtkcsscornervalueprivate.h"
23 #include "gtkcsstypesprivate.h"
24 #include "gtkstylecontextprivate.h"
25 #include "gtkthemingengineprivate.h"
30 * _gtk_rounded_box_init_rect:
31 * @box: box to initialize
32 * @x: x coordinate of box
33 * @y: y coordinate of box
34 * @width: width of box
35 * @height: height of box
37 * Initializes the given @box to represent the given rectangle.
41 _gtk_rounded_box_init_rect (GtkRoundedBox *box,
47 memset (box, 0, sizeof (GtkRoundedBox));
51 box->box.width = width;
52 box->box.height = height;
55 /* clamp border radius, following CSS specs */
57 gtk_rounded_box_clamp_border_radius (GtkRoundedBox *box)
61 /* note: division by zero leads to +INF, which is > factor, so will be ignored */
62 factor = MIN (factor, box->box.width / (box->corner[GTK_CSS_TOP_LEFT].horizontal +
63 box->corner[GTK_CSS_TOP_RIGHT].horizontal));
64 factor = MIN (factor, box->box.height / (box->corner[GTK_CSS_TOP_RIGHT].vertical +
65 box->corner[GTK_CSS_BOTTOM_RIGHT].vertical));
66 factor = MIN (factor, box->box.width / (box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal +
67 box->corner[GTK_CSS_BOTTOM_LEFT].horizontal));
68 factor = MIN (factor, box->box.height / (box->corner[GTK_CSS_TOP_LEFT].vertical +
69 box->corner[GTK_CSS_BOTTOM_LEFT].vertical));
71 box->corner[GTK_CSS_TOP_LEFT].horizontal *= factor;
72 box->corner[GTK_CSS_TOP_LEFT].vertical *= factor;
73 box->corner[GTK_CSS_TOP_RIGHT].horizontal *= factor;
74 box->corner[GTK_CSS_TOP_RIGHT].vertical *= factor;
75 box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal *= factor;
76 box->corner[GTK_CSS_BOTTOM_RIGHT].vertical *= factor;
77 box->corner[GTK_CSS_BOTTOM_LEFT].horizontal *= factor;
78 box->corner[GTK_CSS_BOTTOM_LEFT].vertical *= factor;
82 _gtk_rounded_box_apply_border_radius (GtkRoundedBox *box,
84 GtkJunctionSides junction)
86 if (corner[GTK_CSS_TOP_LEFT] && (junction & GTK_JUNCTION_CORNER_TOPLEFT) == 0)
88 box->corner[GTK_CSS_TOP_LEFT].horizontal = _gtk_css_corner_value_get_x (corner[GTK_CSS_TOP_LEFT],
90 box->corner[GTK_CSS_TOP_LEFT].vertical = _gtk_css_corner_value_get_y (corner[GTK_CSS_TOP_LEFT],
93 if (corner[GTK_CSS_TOP_RIGHT] && (junction & GTK_JUNCTION_CORNER_TOPRIGHT) == 0)
95 box->corner[GTK_CSS_TOP_RIGHT].horizontal = _gtk_css_corner_value_get_x (corner[GTK_CSS_TOP_RIGHT],
97 box->corner[GTK_CSS_TOP_RIGHT].vertical = _gtk_css_corner_value_get_y (corner[GTK_CSS_TOP_RIGHT],
100 if (corner[GTK_CSS_BOTTOM_RIGHT] && (junction & GTK_JUNCTION_CORNER_BOTTOMRIGHT) == 0)
102 box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal = _gtk_css_corner_value_get_x (corner[GTK_CSS_BOTTOM_RIGHT],
104 box->corner[GTK_CSS_BOTTOM_RIGHT].vertical = _gtk_css_corner_value_get_y (corner[GTK_CSS_BOTTOM_RIGHT],
107 if (corner[GTK_CSS_BOTTOM_LEFT] && (junction & GTK_JUNCTION_CORNER_BOTTOMLEFT) == 0)
109 box->corner[GTK_CSS_BOTTOM_LEFT].horizontal = _gtk_css_corner_value_get_x (corner[GTK_CSS_BOTTOM_LEFT],
111 box->corner[GTK_CSS_BOTTOM_LEFT].vertical = _gtk_css_corner_value_get_y (corner[GTK_CSS_BOTTOM_LEFT],
115 gtk_rounded_box_clamp_border_radius (box);
119 _gtk_rounded_box_apply_border_radius_for_context (GtkRoundedBox *box,
120 GtkStyleContext *context,
121 GtkJunctionSides junction)
123 GtkCssValue *corner[4];
125 corner[GTK_CSS_TOP_LEFT] = _gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_TOP_LEFT_RADIUS);
126 corner[GTK_CSS_TOP_RIGHT] = _gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_TOP_RIGHT_RADIUS);
127 corner[GTK_CSS_BOTTOM_LEFT] = _gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_BOTTOM_LEFT_RADIUS);
128 corner[GTK_CSS_BOTTOM_RIGHT] = _gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_BOTTOM_RIGHT_RADIUS);
130 _gtk_rounded_box_apply_border_radius (box, corner, junction);
134 _gtk_rounded_box_apply_border_radius_for_engine (GtkRoundedBox *box,
135 GtkThemingEngine *engine,
136 GtkJunctionSides junction)
138 _gtk_rounded_box_apply_border_radius_for_context (box, _gtk_theming_engine_get_context (engine), junction);
142 gtk_css_border_radius_grow (GtkRoundedBoxCorner *corner,
146 corner->horizontal += horizontal;
147 corner->vertical += vertical;
149 if (corner->horizontal <= 0 || corner->vertical <= 0)
151 corner->horizontal = 0;
152 corner->vertical = 0;
156 _gtk_rounded_box_grow (GtkRoundedBox *box,
162 if (box->box.width + left + right < 0)
164 box->box.x -= left * box->box.width / (left + right);
170 box->box.width += left + right;
173 if (box->box.height + bottom + right < 0)
175 box->box.y -= top * box->box.height / (top + bottom);
181 box->box.height += top + bottom;
184 gtk_css_border_radius_grow (&box->corner[GTK_CSS_TOP_LEFT], left, top);
185 gtk_css_border_radius_grow (&box->corner[GTK_CSS_TOP_RIGHT], right, bottom);
186 gtk_css_border_radius_grow (&box->corner[GTK_CSS_BOTTOM_RIGHT], right, top);
187 gtk_css_border_radius_grow (&box->corner[GTK_CSS_BOTTOM_LEFT], left, bottom);
191 _gtk_rounded_box_shrink (GtkRoundedBox *box,
197 _gtk_rounded_box_grow (box, -top, -right, -bottom, -left);
201 _gtk_rounded_box_move (GtkRoundedBox *box,
210 _cairo_ellipsis (cairo_t *cr,
211 double xc, double yc,
212 double xradius, double yradius,
213 double angle1, double angle2)
215 if (xradius <= 0.0 || yradius <= 0.0)
217 cairo_line_to (cr, xc, yc);
222 cairo_translate (cr, xc, yc);
223 cairo_scale (cr, xradius, yradius);
224 cairo_arc (cr, 0, 0, 1.0, angle1, angle2);
229 _cairo_ellipsis_negative (cairo_t *cr,
230 double xc, double yc,
231 double xradius, double yradius,
232 double angle1, double angle2)
234 if (xradius <= 0.0 || yradius <= 0.0)
236 cairo_line_to (cr, xc, yc);
241 cairo_translate (cr, xc, yc);
242 cairo_scale (cr, xradius, yradius);
243 cairo_arc_negative (cr, 0, 0, 1.0, angle1, angle2);
248 _gtk_rounded_box_path (const GtkRoundedBox *box,
251 cairo_new_sub_path (cr);
254 box->box.x + box->corner[GTK_CSS_TOP_LEFT].horizontal,
255 box->box.y + box->corner[GTK_CSS_TOP_LEFT].vertical,
256 box->corner[GTK_CSS_TOP_LEFT].horizontal,
257 box->corner[GTK_CSS_TOP_LEFT].vertical,
260 box->box.x + box->box.width - box->corner[GTK_CSS_TOP_RIGHT].horizontal,
261 box->box.y + box->corner[GTK_CSS_TOP_RIGHT].vertical,
262 box->corner[GTK_CSS_TOP_RIGHT].horizontal,
263 box->corner[GTK_CSS_TOP_RIGHT].vertical,
266 box->box.x + box->box.width - box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
267 box->box.y + box->box.height - box->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
268 box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
269 box->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
272 box->box.x + box->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
273 box->box.y + box->box.height - box->corner[GTK_CSS_BOTTOM_LEFT].vertical,
274 box->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
275 box->corner[GTK_CSS_BOTTOM_LEFT].vertical,
278 cairo_close_path (cr);
282 _gtk_rounded_box_guess_length (const GtkRoundedBox *box,
286 GtkCssCorner before, after;
289 after = (side + 1) % 4;
292 length = box->box.height
293 - box->corner[before].vertical
294 - box->corner[after].vertical;
296 length = box->box.width
297 - box->corner[before].horizontal
298 - box->corner[after].horizontal;
300 length += G_PI * 0.125 * (box->corner[before].horizontal
301 + box->corner[before].vertical
302 + box->corner[after].horizontal
303 + box->corner[after].vertical);
309 _gtk_rounded_box_path_side (const GtkRoundedBox *box,
317 box->box.x + box->corner[GTK_CSS_TOP_LEFT].horizontal,
318 box->box.y + box->corner[GTK_CSS_TOP_LEFT].vertical,
319 box->corner[GTK_CSS_TOP_LEFT].horizontal,
320 box->corner[GTK_CSS_TOP_LEFT].vertical,
321 5 * G_PI / 4, 3 * G_PI / 2);
323 box->box.x + box->box.width - box->corner[GTK_CSS_TOP_RIGHT].horizontal,
324 box->box.y + box->corner[GTK_CSS_TOP_RIGHT].vertical,
325 box->corner[GTK_CSS_TOP_RIGHT].horizontal,
326 box->corner[GTK_CSS_TOP_RIGHT].vertical,
327 - G_PI / 2, -G_PI / 4);
331 box->box.x + box->box.width - box->corner[GTK_CSS_TOP_RIGHT].horizontal,
332 box->box.y + box->corner[GTK_CSS_TOP_RIGHT].vertical,
333 box->corner[GTK_CSS_TOP_RIGHT].horizontal,
334 box->corner[GTK_CSS_TOP_RIGHT].vertical,
337 box->box.x + box->box.width - box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
338 box->box.y + box->box.height - box->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
339 box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
340 box->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
345 box->box.x + box->box.width - box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
346 box->box.y + box->box.height - box->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
347 box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
348 box->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
351 box->box.x + box->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
352 box->box.y + box->box.height - box->corner[GTK_CSS_BOTTOM_LEFT].vertical,
353 box->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
354 box->corner[GTK_CSS_BOTTOM_LEFT].vertical,
355 G_PI / 2, 3 * G_PI / 4);
359 box->box.x + box->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
360 box->box.y + box->box.height - box->corner[GTK_CSS_BOTTOM_LEFT].vertical,
361 box->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
362 box->corner[GTK_CSS_BOTTOM_LEFT].vertical,
365 box->box.x + box->corner[GTK_CSS_TOP_LEFT].horizontal,
366 box->box.y + box->corner[GTK_CSS_TOP_LEFT].vertical,
367 box->corner[GTK_CSS_TOP_LEFT].horizontal,
368 box->corner[GTK_CSS_TOP_LEFT].vertical,
372 g_assert_not_reached ();
378 _gtk_rounded_box_path_top (const GtkRoundedBox *outer,
379 const GtkRoundedBox *inner,
382 cairo_new_sub_path (cr);
385 outer->box.x + outer->corner[GTK_CSS_TOP_LEFT].horizontal,
386 outer->box.y + outer->corner[GTK_CSS_TOP_LEFT].vertical,
387 outer->corner[GTK_CSS_TOP_LEFT].horizontal,
388 outer->corner[GTK_CSS_TOP_LEFT].vertical,
389 5 * G_PI / 4, 3 * G_PI / 2);
391 outer->box.x + outer->box.width - outer->corner[GTK_CSS_TOP_RIGHT].horizontal,
392 outer->box.y + outer->corner[GTK_CSS_TOP_RIGHT].vertical,
393 outer->corner[GTK_CSS_TOP_RIGHT].horizontal,
394 outer->corner[GTK_CSS_TOP_RIGHT].vertical,
395 - G_PI / 2, -G_PI / 4);
397 _cairo_ellipsis_negative (cr,
398 inner->box.x + inner->box.width - inner->corner[GTK_CSS_TOP_RIGHT].horizontal,
399 inner->box.y + inner->corner[GTK_CSS_TOP_RIGHT].vertical,
400 inner->corner[GTK_CSS_TOP_RIGHT].horizontal,
401 inner->corner[GTK_CSS_TOP_RIGHT].vertical,
402 -G_PI / 4, - G_PI / 2);
403 _cairo_ellipsis_negative (cr,
404 inner->box.x + inner->corner[GTK_CSS_TOP_LEFT].horizontal,
405 inner->box.y + inner->corner[GTK_CSS_TOP_LEFT].vertical,
406 inner->corner[GTK_CSS_TOP_LEFT].horizontal,
407 inner->corner[GTK_CSS_TOP_LEFT].vertical,
408 3 * G_PI / 2, 5 * G_PI / 4);
410 cairo_close_path (cr);
414 _gtk_rounded_box_path_right (const GtkRoundedBox *outer,
415 const GtkRoundedBox *inner,
418 cairo_new_sub_path (cr);
421 outer->box.x + outer->box.width - outer->corner[GTK_CSS_TOP_RIGHT].horizontal,
422 outer->box.y + outer->corner[GTK_CSS_TOP_RIGHT].vertical,
423 outer->corner[GTK_CSS_TOP_RIGHT].horizontal,
424 outer->corner[GTK_CSS_TOP_RIGHT].vertical,
427 outer->box.x + outer->box.width - outer->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
428 outer->box.y + outer->box.height - outer->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
429 outer->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
430 outer->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
433 _cairo_ellipsis_negative (cr,
434 inner->box.x + inner->box.width - inner->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
435 inner->box.y + inner->box.height - inner->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
436 inner->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
437 inner->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
439 _cairo_ellipsis_negative (cr,
440 inner->box.x + inner->box.width - inner->corner[GTK_CSS_TOP_RIGHT].horizontal,
441 inner->box.y + inner->corner[GTK_CSS_TOP_RIGHT].vertical,
442 inner->corner[GTK_CSS_TOP_RIGHT].horizontal,
443 inner->corner[GTK_CSS_TOP_RIGHT].vertical,
446 cairo_close_path (cr);
450 _gtk_rounded_box_path_bottom (const GtkRoundedBox *outer,
451 const GtkRoundedBox *inner,
454 cairo_new_sub_path (cr);
457 outer->box.x + outer->box.width - outer->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
458 outer->box.y + outer->box.height - outer->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
459 outer->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
460 outer->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
463 outer->box.x + outer->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
464 outer->box.y + outer->box.height - outer->corner[GTK_CSS_BOTTOM_LEFT].vertical,
465 outer->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
466 outer->corner[GTK_CSS_BOTTOM_LEFT].vertical,
467 G_PI / 2, 3 * G_PI / 4);
469 _cairo_ellipsis_negative (cr,
470 inner->box.x + inner->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
471 inner->box.y + inner->box.height - inner->corner[GTK_CSS_BOTTOM_LEFT].vertical,
472 inner->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
473 inner->corner[GTK_CSS_BOTTOM_LEFT].vertical,
474 3 * G_PI / 4, G_PI / 2);
475 _cairo_ellipsis_negative (cr,
476 inner->box.x + inner->box.width - inner->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
477 inner->box.y + inner->box.height - inner->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
478 inner->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
479 inner->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
482 cairo_close_path (cr);
486 _gtk_rounded_box_path_left (const GtkRoundedBox *outer,
487 const GtkRoundedBox *inner,
490 cairo_new_sub_path (cr);
493 outer->box.x + outer->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
494 outer->box.y + outer->box.height - outer->corner[GTK_CSS_BOTTOM_LEFT].vertical,
495 outer->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
496 outer->corner[GTK_CSS_BOTTOM_LEFT].vertical,
499 outer->box.x + outer->corner[GTK_CSS_TOP_LEFT].horizontal,
500 outer->box.y + outer->corner[GTK_CSS_TOP_LEFT].vertical,
501 outer->corner[GTK_CSS_TOP_LEFT].horizontal,
502 outer->corner[GTK_CSS_TOP_LEFT].vertical,
505 _cairo_ellipsis_negative (cr,
506 inner->box.x + inner->corner[GTK_CSS_TOP_LEFT].horizontal,
507 inner->box.y + inner->corner[GTK_CSS_TOP_LEFT].vertical,
508 inner->corner[GTK_CSS_TOP_LEFT].horizontal,
509 inner->corner[GTK_CSS_TOP_LEFT].vertical,
511 _cairo_ellipsis_negative (cr,
512 inner->box.x + inner->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
513 inner->box.y + inner->box.height - inner->corner[GTK_CSS_BOTTOM_LEFT].vertical,
514 inner->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
515 inner->corner[GTK_CSS_BOTTOM_LEFT].vertical,
518 cairo_close_path (cr);
522 _gtk_rounded_box_clip_path (const GtkRoundedBox *box,
526 box->box.x, box->box.y,
527 box->box.width, box->box.height);