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"
21 #include "gtkthemingengineprivate.h"
26 * _gtk_rounded_box_init_rect:
27 * @box: box to initialize
28 * @x: x coordinate of box
29 * @y: y coordinate of box
30 * @width: width of box
31 * @height: height of box
33 * Initializes the given @box to represent the given rectangle.
37 _gtk_rounded_box_init_rect (GtkRoundedBox *box,
43 memset (box, 0, sizeof (GtkRoundedBox));
47 box->box.width = width;
48 box->box.height = height;
51 /* clamp border radius, following CSS specs */
53 gtk_rounded_box_clamp_border_radius (GtkRoundedBox *box)
57 /* note: division by zero leads to +INF, which is > factor, so will be ignored */
58 factor = MIN (factor, box->box.width / (box->corner[GTK_CSS_TOP_LEFT].horizontal +
59 box->corner[GTK_CSS_TOP_RIGHT].horizontal));
60 factor = MIN (factor, box->box.height / (box->corner[GTK_CSS_TOP_RIGHT].vertical +
61 box->corner[GTK_CSS_BOTTOM_RIGHT].vertical));
62 factor = MIN (factor, box->box.width / (box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal +
63 box->corner[GTK_CSS_BOTTOM_LEFT].horizontal));
64 factor = MIN (factor, box->box.height / (box->corner[GTK_CSS_TOP_LEFT].vertical +
65 box->corner[GTK_CSS_BOTTOM_LEFT].vertical));
67 box->corner[GTK_CSS_TOP_LEFT].horizontal *= factor;
68 box->corner[GTK_CSS_TOP_LEFT].vertical *= factor;
69 box->corner[GTK_CSS_TOP_RIGHT].horizontal *= factor;
70 box->corner[GTK_CSS_TOP_RIGHT].vertical *= factor;
71 box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal *= factor;
72 box->corner[GTK_CSS_BOTTOM_RIGHT].vertical *= factor;
73 box->corner[GTK_CSS_BOTTOM_LEFT].horizontal *= factor;
74 box->corner[GTK_CSS_BOTTOM_LEFT].vertical *= factor;
78 _gtk_rounded_box_apply_border_radius (GtkRoundedBox *box,
79 GtkCssBorderCornerRadius **corner,
80 GtkJunctionSides junction)
82 if (corner[GTK_CSS_TOP_LEFT] && (junction & GTK_JUNCTION_CORNER_TOPLEFT) == 0)
84 box->corner[GTK_CSS_TOP_LEFT].horizontal = _gtk_css_number_get (&corner[GTK_CSS_TOP_LEFT]->horizontal,
86 box->corner[GTK_CSS_TOP_LEFT].vertical = _gtk_css_number_get (&corner[GTK_CSS_TOP_LEFT]->vertical,
89 if (corner[GTK_CSS_TOP_RIGHT] && (junction & GTK_JUNCTION_CORNER_TOPRIGHT) == 0)
91 box->corner[GTK_CSS_TOP_RIGHT].horizontal = _gtk_css_number_get (&corner[GTK_CSS_TOP_RIGHT]->horizontal,
93 box->corner[GTK_CSS_TOP_RIGHT].vertical = _gtk_css_number_get (&corner[GTK_CSS_TOP_RIGHT]->vertical,
96 if (corner[GTK_CSS_BOTTOM_RIGHT] && (junction & GTK_JUNCTION_CORNER_BOTTOMRIGHT) == 0)
98 box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal = _gtk_css_number_get (&corner[GTK_CSS_BOTTOM_RIGHT]->horizontal,
100 box->corner[GTK_CSS_BOTTOM_RIGHT].vertical = _gtk_css_number_get (&corner[GTK_CSS_BOTTOM_RIGHT]->vertical,
103 if (corner[GTK_CSS_BOTTOM_LEFT] && (junction & GTK_JUNCTION_CORNER_BOTTOMLEFT) == 0)
105 box->corner[GTK_CSS_BOTTOM_LEFT].horizontal = _gtk_css_number_get (&corner[GTK_CSS_BOTTOM_LEFT]->horizontal,
107 box->corner[GTK_CSS_BOTTOM_LEFT].vertical = _gtk_css_number_get (&corner[GTK_CSS_BOTTOM_LEFT]->vertical,
111 gtk_rounded_box_clamp_border_radius (box);
115 _gtk_rounded_box_apply_border_radius_for_context (GtkRoundedBox *box,
116 GtkStyleContext *context,
118 GtkJunctionSides junction)
120 GtkCssBorderCornerRadius *corner[4];
123 gtk_style_context_get (context, state,
124 /* Can't use border-radius as it's an int for
125 * backwards compat */
126 "border-top-left-radius", &corner[GTK_CSS_TOP_LEFT],
127 "border-top-right-radius", &corner[GTK_CSS_TOP_RIGHT],
128 "border-bottom-right-radius", &corner[GTK_CSS_BOTTOM_RIGHT],
129 "border-bottom-left-radius", &corner[GTK_CSS_BOTTOM_LEFT],
132 _gtk_rounded_box_apply_border_radius (box, corner, junction);
134 for (i = 0; i < 4; i++)
139 _gtk_rounded_box_apply_border_radius_for_engine (GtkRoundedBox *box,
140 GtkThemingEngine *engine,
142 GtkJunctionSides junction)
144 _gtk_rounded_box_apply_border_radius_for_context (box, _gtk_theming_engine_get_context (engine),
149 gtk_css_border_radius_grow (GtkRoundedBoxCorner *corner,
153 corner->horizontal += horizontal;
154 corner->vertical += vertical;
156 if (corner->horizontal <= 0 || corner->vertical <= 0)
158 corner->horizontal = 0;
159 corner->vertical = 0;
163 _gtk_rounded_box_grow (GtkRoundedBox *box,
169 if (box->box.width + left + right < 0)
171 box->box.x -= left * box->box.width / (left + right);
177 box->box.width += left + right;
180 if (box->box.height + bottom + right < 0)
182 box->box.y -= top * box->box.height / (top + bottom);
188 box->box.height += top + bottom;
191 gtk_css_border_radius_grow (&box->corner[GTK_CSS_TOP_LEFT], left, top);
192 gtk_css_border_radius_grow (&box->corner[GTK_CSS_TOP_RIGHT], right, bottom);
193 gtk_css_border_radius_grow (&box->corner[GTK_CSS_BOTTOM_RIGHT], right, top);
194 gtk_css_border_radius_grow (&box->corner[GTK_CSS_BOTTOM_LEFT], left, bottom);
198 _gtk_rounded_box_shrink (GtkRoundedBox *box,
204 _gtk_rounded_box_grow (box, -top, -right, -bottom, -left);
208 _gtk_rounded_box_move (GtkRoundedBox *box,
217 _cairo_ellipsis (cairo_t *cr,
218 double xc, double yc,
219 double xradius, double yradius,
220 double angle1, double angle2)
222 if (xradius <= 0.0 || yradius <= 0.0)
224 cairo_line_to (cr, xc, yc);
229 cairo_translate (cr, xc, yc);
230 cairo_scale (cr, xradius, yradius);
231 cairo_arc (cr, 0, 0, 1.0, angle1, angle2);
236 _cairo_ellipsis_negative (cairo_t *cr,
237 double xc, double yc,
238 double xradius, double yradius,
239 double angle1, double angle2)
241 if (xradius <= 0.0 || yradius <= 0.0)
243 cairo_line_to (cr, xc, yc);
248 cairo_translate (cr, xc, yc);
249 cairo_scale (cr, xradius, yradius);
250 cairo_arc_negative (cr, 0, 0, 1.0, angle1, angle2);
255 _gtk_rounded_box_path (const GtkRoundedBox *box,
258 cairo_new_sub_path (cr);
261 box->box.x + box->corner[GTK_CSS_TOP_LEFT].horizontal,
262 box->box.y + box->corner[GTK_CSS_TOP_LEFT].vertical,
263 box->corner[GTK_CSS_TOP_LEFT].horizontal,
264 box->corner[GTK_CSS_TOP_LEFT].vertical,
267 box->box.x + box->box.width - box->corner[GTK_CSS_TOP_RIGHT].horizontal,
268 box->box.y + box->corner[GTK_CSS_TOP_RIGHT].vertical,
269 box->corner[GTK_CSS_TOP_RIGHT].horizontal,
270 box->corner[GTK_CSS_TOP_RIGHT].vertical,
273 box->box.x + box->box.width - box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
274 box->box.y + box->box.height - box->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
275 box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
276 box->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
279 box->box.x + box->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
280 box->box.y + box->box.height - box->corner[GTK_CSS_BOTTOM_LEFT].vertical,
281 box->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
282 box->corner[GTK_CSS_BOTTOM_LEFT].vertical,
285 cairo_close_path (cr);
289 _gtk_rounded_box_guess_length (const GtkRoundedBox *box,
293 GtkCssCorner before, after;
296 after = (side + 1) % 4;
299 length = box->box.height
300 - box->corner[before].vertical
301 - box->corner[after].vertical;
303 length = box->box.width
304 - box->corner[before].horizontal
305 - box->corner[after].horizontal;
307 length += G_PI * 0.125 * (box->corner[before].horizontal
308 + box->corner[before].vertical
309 + box->corner[after].horizontal
310 + box->corner[after].vertical);
316 _gtk_rounded_box_path_side (const GtkRoundedBox *box,
324 box->box.x + box->corner[GTK_CSS_TOP_LEFT].horizontal,
325 box->box.y + box->corner[GTK_CSS_TOP_LEFT].vertical,
326 box->corner[GTK_CSS_TOP_LEFT].horizontal,
327 box->corner[GTK_CSS_TOP_LEFT].vertical,
328 5 * G_PI / 4, 3 * G_PI / 2);
330 box->box.x + box->box.width - box->corner[GTK_CSS_TOP_RIGHT].horizontal,
331 box->box.y + box->corner[GTK_CSS_TOP_RIGHT].vertical,
332 box->corner[GTK_CSS_TOP_RIGHT].horizontal,
333 box->corner[GTK_CSS_TOP_RIGHT].vertical,
334 - G_PI / 2, -G_PI / 4);
338 box->box.x + box->box.width - box->corner[GTK_CSS_TOP_RIGHT].horizontal,
339 box->box.y + box->corner[GTK_CSS_TOP_RIGHT].vertical,
340 box->corner[GTK_CSS_TOP_RIGHT].horizontal,
341 box->corner[GTK_CSS_TOP_RIGHT].vertical,
344 box->box.x + box->box.width - box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
345 box->box.y + box->box.height - box->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
346 box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
347 box->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
352 box->box.x + box->box.width - box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
353 box->box.y + box->box.height - box->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
354 box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
355 box->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
358 box->box.x + box->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
359 box->box.y + box->box.height - box->corner[GTK_CSS_BOTTOM_LEFT].vertical,
360 box->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
361 box->corner[GTK_CSS_BOTTOM_LEFT].vertical,
362 G_PI / 2, 3 * G_PI / 4);
366 box->box.x + box->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
367 box->box.y + box->box.height - box->corner[GTK_CSS_BOTTOM_LEFT].vertical,
368 box->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
369 box->corner[GTK_CSS_BOTTOM_LEFT].vertical,
372 box->box.x + box->corner[GTK_CSS_TOP_LEFT].horizontal,
373 box->box.y + box->corner[GTK_CSS_TOP_LEFT].vertical,
374 box->corner[GTK_CSS_TOP_LEFT].horizontal,
375 box->corner[GTK_CSS_TOP_LEFT].vertical,
379 g_assert_not_reached ();
385 _gtk_rounded_box_path_top (const GtkRoundedBox *outer,
386 const GtkRoundedBox *inner,
389 cairo_new_sub_path (cr);
392 outer->box.x + outer->corner[GTK_CSS_TOP_LEFT].horizontal,
393 outer->box.y + outer->corner[GTK_CSS_TOP_LEFT].vertical,
394 outer->corner[GTK_CSS_TOP_LEFT].horizontal,
395 outer->corner[GTK_CSS_TOP_LEFT].vertical,
396 5 * G_PI / 4, 3 * G_PI / 2);
398 outer->box.x + outer->box.width - outer->corner[GTK_CSS_TOP_RIGHT].horizontal,
399 outer->box.y + outer->corner[GTK_CSS_TOP_RIGHT].vertical,
400 outer->corner[GTK_CSS_TOP_RIGHT].horizontal,
401 outer->corner[GTK_CSS_TOP_RIGHT].vertical,
402 - G_PI / 2, -G_PI / 4);
404 _cairo_ellipsis_negative (cr,
405 inner->box.x + inner->box.width - inner->corner[GTK_CSS_TOP_RIGHT].horizontal,
406 inner->box.y + inner->corner[GTK_CSS_TOP_RIGHT].vertical,
407 inner->corner[GTK_CSS_TOP_RIGHT].horizontal,
408 inner->corner[GTK_CSS_TOP_RIGHT].vertical,
409 -G_PI / 4, - G_PI / 2);
410 _cairo_ellipsis_negative (cr,
411 inner->box.x + inner->corner[GTK_CSS_TOP_LEFT].horizontal,
412 inner->box.y + inner->corner[GTK_CSS_TOP_LEFT].vertical,
413 inner->corner[GTK_CSS_TOP_LEFT].horizontal,
414 inner->corner[GTK_CSS_TOP_LEFT].vertical,
415 3 * G_PI / 2, 5 * G_PI / 4);
417 cairo_close_path (cr);
421 _gtk_rounded_box_path_right (const GtkRoundedBox *outer,
422 const GtkRoundedBox *inner,
425 cairo_new_sub_path (cr);
428 outer->box.x + outer->box.width - outer->corner[GTK_CSS_TOP_RIGHT].horizontal,
429 outer->box.y + outer->corner[GTK_CSS_TOP_RIGHT].vertical,
430 outer->corner[GTK_CSS_TOP_RIGHT].horizontal,
431 outer->corner[GTK_CSS_TOP_RIGHT].vertical,
434 outer->box.x + outer->box.width - outer->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
435 outer->box.y + outer->box.height - outer->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
436 outer->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
437 outer->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
440 _cairo_ellipsis_negative (cr,
441 inner->box.x + inner->box.width - inner->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
442 inner->box.y + inner->box.height - inner->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
443 inner->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
444 inner->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
446 _cairo_ellipsis_negative (cr,
447 inner->box.x + inner->box.width - inner->corner[GTK_CSS_TOP_RIGHT].horizontal,
448 inner->box.y + inner->corner[GTK_CSS_TOP_RIGHT].vertical,
449 inner->corner[GTK_CSS_TOP_RIGHT].horizontal,
450 inner->corner[GTK_CSS_TOP_RIGHT].vertical,
453 cairo_close_path (cr);
457 _gtk_rounded_box_path_bottom (const GtkRoundedBox *outer,
458 const GtkRoundedBox *inner,
461 cairo_new_sub_path (cr);
464 outer->box.x + outer->box.width - outer->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
465 outer->box.y + outer->box.height - outer->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
466 outer->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
467 outer->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
470 outer->box.x + outer->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
471 outer->box.y + outer->box.height - outer->corner[GTK_CSS_BOTTOM_LEFT].vertical,
472 outer->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
473 outer->corner[GTK_CSS_BOTTOM_LEFT].vertical,
474 G_PI / 2, 3 * G_PI / 4);
476 _cairo_ellipsis_negative (cr,
477 inner->box.x + inner->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
478 inner->box.y + inner->box.height - inner->corner[GTK_CSS_BOTTOM_LEFT].vertical,
479 inner->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
480 inner->corner[GTK_CSS_BOTTOM_LEFT].vertical,
481 3 * G_PI / 4, G_PI / 2);
482 _cairo_ellipsis_negative (cr,
483 inner->box.x + inner->box.width - inner->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
484 inner->box.y + inner->box.height - inner->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
485 inner->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
486 inner->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
489 cairo_close_path (cr);
493 _gtk_rounded_box_path_left (const GtkRoundedBox *outer,
494 const GtkRoundedBox *inner,
497 cairo_new_sub_path (cr);
500 outer->box.x + outer->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
501 outer->box.y + outer->box.height - outer->corner[GTK_CSS_BOTTOM_LEFT].vertical,
502 outer->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
503 outer->corner[GTK_CSS_BOTTOM_LEFT].vertical,
506 outer->box.x + outer->corner[GTK_CSS_TOP_LEFT].horizontal,
507 outer->box.y + outer->corner[GTK_CSS_TOP_LEFT].vertical,
508 outer->corner[GTK_CSS_TOP_LEFT].horizontal,
509 outer->corner[GTK_CSS_TOP_LEFT].vertical,
512 _cairo_ellipsis_negative (cr,
513 inner->box.x + inner->corner[GTK_CSS_TOP_LEFT].horizontal,
514 inner->box.y + inner->corner[GTK_CSS_TOP_LEFT].vertical,
515 inner->corner[GTK_CSS_TOP_LEFT].horizontal,
516 inner->corner[GTK_CSS_TOP_LEFT].vertical,
518 _cairo_ellipsis_negative (cr,
519 inner->box.x + inner->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
520 inner->box.y + inner->box.height - inner->corner[GTK_CSS_BOTTOM_LEFT].vertical,
521 inner->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
522 inner->corner[GTK_CSS_BOTTOM_LEFT].vertical,
525 cairo_close_path (cr);
529 _gtk_rounded_box_clip_path (const GtkRoundedBox *box,
533 box->box.x, box->box.y,
534 box->box.width, box->box.height);