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;
157 _gtk_rounded_box_grow (GtkRoundedBox *box,
163 if (box->box.width + left + right < 0)
165 box->box.x -= left * box->box.width / (left + right);
171 box->box.width += left + right;
174 if (box->box.height + bottom + top < 0)
176 box->box.y -= top * box->box.height / (top + bottom);
182 box->box.height += top + bottom;
185 gtk_css_border_radius_grow (&box->corner[GTK_CSS_TOP_LEFT], left, top);
186 gtk_css_border_radius_grow (&box->corner[GTK_CSS_TOP_RIGHT], right, bottom);
187 gtk_css_border_radius_grow (&box->corner[GTK_CSS_BOTTOM_RIGHT], right, top);
188 gtk_css_border_radius_grow (&box->corner[GTK_CSS_BOTTOM_LEFT], left, bottom);
192 _gtk_rounded_box_shrink (GtkRoundedBox *box,
198 _gtk_rounded_box_grow (box, -top, -right, -bottom, -left);
202 _gtk_rounded_box_move (GtkRoundedBox *box,
211 _cairo_ellipsis (cairo_t *cr,
212 double xc, double yc,
213 double xradius, double yradius,
214 double angle1, double angle2)
216 if (xradius <= 0.0 || yradius <= 0.0)
218 cairo_line_to (cr, xc, yc);
223 cairo_translate (cr, xc, yc);
224 cairo_scale (cr, xradius, yradius);
225 cairo_arc (cr, 0, 0, 1.0, angle1, angle2);
230 _cairo_ellipsis_negative (cairo_t *cr,
231 double xc, double yc,
232 double xradius, double yradius,
233 double angle1, double angle2)
235 if (xradius <= 0.0 || yradius <= 0.0)
237 cairo_line_to (cr, xc, yc);
242 cairo_translate (cr, xc, yc);
243 cairo_scale (cr, xradius, yradius);
244 cairo_arc_negative (cr, 0, 0, 1.0, angle1, angle2);
249 _gtk_rounded_box_path (const GtkRoundedBox *box,
252 cairo_new_sub_path (cr);
255 box->box.x + box->corner[GTK_CSS_TOP_LEFT].horizontal,
256 box->box.y + box->corner[GTK_CSS_TOP_LEFT].vertical,
257 box->corner[GTK_CSS_TOP_LEFT].horizontal,
258 box->corner[GTK_CSS_TOP_LEFT].vertical,
261 box->box.x + box->box.width - box->corner[GTK_CSS_TOP_RIGHT].horizontal,
262 box->box.y + box->corner[GTK_CSS_TOP_RIGHT].vertical,
263 box->corner[GTK_CSS_TOP_RIGHT].horizontal,
264 box->corner[GTK_CSS_TOP_RIGHT].vertical,
267 box->box.x + box->box.width - box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
268 box->box.y + box->box.height - box->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
269 box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
270 box->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
273 box->box.x + box->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
274 box->box.y + box->box.height - box->corner[GTK_CSS_BOTTOM_LEFT].vertical,
275 box->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
276 box->corner[GTK_CSS_BOTTOM_LEFT].vertical,
279 cairo_close_path (cr);
283 _gtk_rounded_box_guess_length (const GtkRoundedBox *box,
287 GtkCssSide before, after;
290 after = (side + 1) % 4;
293 length = box->box.height
294 - box->corner[before].vertical
295 - box->corner[after].vertical;
297 length = box->box.width
298 - box->corner[before].horizontal
299 - box->corner[after].horizontal;
301 length += G_PI * 0.125 * (box->corner[before].horizontal
302 + box->corner[before].vertical
303 + box->corner[after].horizontal
304 + box->corner[after].vertical);
310 _gtk_rounded_box_path_side (const GtkRoundedBox *box,
318 box->box.x + box->corner[GTK_CSS_TOP_LEFT].horizontal,
319 box->box.y + box->corner[GTK_CSS_TOP_LEFT].vertical,
320 box->corner[GTK_CSS_TOP_LEFT].horizontal,
321 box->corner[GTK_CSS_TOP_LEFT].vertical,
322 5 * G_PI / 4, 3 * G_PI / 2);
324 box->box.x + box->box.width - box->corner[GTK_CSS_TOP_RIGHT].horizontal,
325 box->box.y + box->corner[GTK_CSS_TOP_RIGHT].vertical,
326 box->corner[GTK_CSS_TOP_RIGHT].horizontal,
327 box->corner[GTK_CSS_TOP_RIGHT].vertical,
328 - G_PI / 2, -G_PI / 4);
332 box->box.x + box->box.width - box->corner[GTK_CSS_TOP_RIGHT].horizontal,
333 box->box.y + box->corner[GTK_CSS_TOP_RIGHT].vertical,
334 box->corner[GTK_CSS_TOP_RIGHT].horizontal,
335 box->corner[GTK_CSS_TOP_RIGHT].vertical,
338 box->box.x + box->box.width - box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
339 box->box.y + box->box.height - box->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
340 box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
341 box->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
346 box->box.x + box->box.width - box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
347 box->box.y + box->box.height - box->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
348 box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
349 box->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
352 box->box.x + box->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
353 box->box.y + box->box.height - box->corner[GTK_CSS_BOTTOM_LEFT].vertical,
354 box->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
355 box->corner[GTK_CSS_BOTTOM_LEFT].vertical,
356 G_PI / 2, 3 * G_PI / 4);
360 box->box.x + box->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
361 box->box.y + box->box.height - box->corner[GTK_CSS_BOTTOM_LEFT].vertical,
362 box->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
363 box->corner[GTK_CSS_BOTTOM_LEFT].vertical,
366 box->box.x + box->corner[GTK_CSS_TOP_LEFT].horizontal,
367 box->box.y + box->corner[GTK_CSS_TOP_LEFT].vertical,
368 box->corner[GTK_CSS_TOP_LEFT].horizontal,
369 box->corner[GTK_CSS_TOP_LEFT].vertical,
373 g_assert_not_reached ();
379 _gtk_rounded_box_path_top (const GtkRoundedBox *outer,
380 const GtkRoundedBox *inner,
383 cairo_new_sub_path (cr);
386 outer->box.x + outer->corner[GTK_CSS_TOP_LEFT].horizontal,
387 outer->box.y + outer->corner[GTK_CSS_TOP_LEFT].vertical,
388 outer->corner[GTK_CSS_TOP_LEFT].horizontal,
389 outer->corner[GTK_CSS_TOP_LEFT].vertical,
390 5 * G_PI / 4, 3 * G_PI / 2);
392 outer->box.x + outer->box.width - outer->corner[GTK_CSS_TOP_RIGHT].horizontal,
393 outer->box.y + outer->corner[GTK_CSS_TOP_RIGHT].vertical,
394 outer->corner[GTK_CSS_TOP_RIGHT].horizontal,
395 outer->corner[GTK_CSS_TOP_RIGHT].vertical,
396 - G_PI / 2, -G_PI / 4);
398 _cairo_ellipsis_negative (cr,
399 inner->box.x + inner->box.width - inner->corner[GTK_CSS_TOP_RIGHT].horizontal,
400 inner->box.y + inner->corner[GTK_CSS_TOP_RIGHT].vertical,
401 inner->corner[GTK_CSS_TOP_RIGHT].horizontal,
402 inner->corner[GTK_CSS_TOP_RIGHT].vertical,
403 -G_PI / 4, - G_PI / 2);
404 _cairo_ellipsis_negative (cr,
405 inner->box.x + inner->corner[GTK_CSS_TOP_LEFT].horizontal,
406 inner->box.y + inner->corner[GTK_CSS_TOP_LEFT].vertical,
407 inner->corner[GTK_CSS_TOP_LEFT].horizontal,
408 inner->corner[GTK_CSS_TOP_LEFT].vertical,
409 3 * G_PI / 2, 5 * G_PI / 4);
411 cairo_close_path (cr);
415 _gtk_rounded_box_path_right (const GtkRoundedBox *outer,
416 const GtkRoundedBox *inner,
419 cairo_new_sub_path (cr);
422 outer->box.x + outer->box.width - outer->corner[GTK_CSS_TOP_RIGHT].horizontal,
423 outer->box.y + outer->corner[GTK_CSS_TOP_RIGHT].vertical,
424 outer->corner[GTK_CSS_TOP_RIGHT].horizontal,
425 outer->corner[GTK_CSS_TOP_RIGHT].vertical,
428 outer->box.x + outer->box.width - outer->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
429 outer->box.y + outer->box.height - outer->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
430 outer->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
431 outer->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
434 _cairo_ellipsis_negative (cr,
435 inner->box.x + inner->box.width - inner->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
436 inner->box.y + inner->box.height - inner->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
437 inner->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
438 inner->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
440 _cairo_ellipsis_negative (cr,
441 inner->box.x + inner->box.width - inner->corner[GTK_CSS_TOP_RIGHT].horizontal,
442 inner->box.y + inner->corner[GTK_CSS_TOP_RIGHT].vertical,
443 inner->corner[GTK_CSS_TOP_RIGHT].horizontal,
444 inner->corner[GTK_CSS_TOP_RIGHT].vertical,
447 cairo_close_path (cr);
451 _gtk_rounded_box_path_bottom (const GtkRoundedBox *outer,
452 const GtkRoundedBox *inner,
455 cairo_new_sub_path (cr);
458 outer->box.x + outer->box.width - outer->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
459 outer->box.y + outer->box.height - outer->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
460 outer->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
461 outer->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
464 outer->box.x + outer->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
465 outer->box.y + outer->box.height - outer->corner[GTK_CSS_BOTTOM_LEFT].vertical,
466 outer->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
467 outer->corner[GTK_CSS_BOTTOM_LEFT].vertical,
468 G_PI / 2, 3 * G_PI / 4);
470 _cairo_ellipsis_negative (cr,
471 inner->box.x + inner->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
472 inner->box.y + inner->box.height - inner->corner[GTK_CSS_BOTTOM_LEFT].vertical,
473 inner->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
474 inner->corner[GTK_CSS_BOTTOM_LEFT].vertical,
475 3 * G_PI / 4, G_PI / 2);
476 _cairo_ellipsis_negative (cr,
477 inner->box.x + inner->box.width - inner->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
478 inner->box.y + inner->box.height - inner->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
479 inner->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
480 inner->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
483 cairo_close_path (cr);
487 _gtk_rounded_box_path_left (const GtkRoundedBox *outer,
488 const GtkRoundedBox *inner,
491 cairo_new_sub_path (cr);
494 outer->box.x + outer->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
495 outer->box.y + outer->box.height - outer->corner[GTK_CSS_BOTTOM_LEFT].vertical,
496 outer->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
497 outer->corner[GTK_CSS_BOTTOM_LEFT].vertical,
500 outer->box.x + outer->corner[GTK_CSS_TOP_LEFT].horizontal,
501 outer->box.y + outer->corner[GTK_CSS_TOP_LEFT].vertical,
502 outer->corner[GTK_CSS_TOP_LEFT].horizontal,
503 outer->corner[GTK_CSS_TOP_LEFT].vertical,
506 _cairo_ellipsis_negative (cr,
507 inner->box.x + inner->corner[GTK_CSS_TOP_LEFT].horizontal,
508 inner->box.y + inner->corner[GTK_CSS_TOP_LEFT].vertical,
509 inner->corner[GTK_CSS_TOP_LEFT].horizontal,
510 inner->corner[GTK_CSS_TOP_LEFT].vertical,
512 _cairo_ellipsis_negative (cr,
513 inner->box.x + inner->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
514 inner->box.y + inner->box.height - inner->corner[GTK_CSS_BOTTOM_LEFT].vertical,
515 inner->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
516 inner->corner[GTK_CSS_BOTTOM_LEFT].vertical,
519 cairo_close_path (cr);
523 _gtk_rounded_box_clip_path (const GtkRoundedBox *box,
527 box->box.x, box->box.y,
528 box->box.width, box->box.height);