]> git.mdlowis.com Git - proto/labwc.git/commitdiff
theme: fix rounded-corner bug
authorJohan Malm <jgm323@gmail.com>
Sat, 15 Jul 2023 20:50:40 +0000 (21:50 +0100)
committerJohan Malm <johanmalm@users.noreply.github.com>
Mon, 17 Jul 2023 19:12:14 +0000 (20:12 +0100)
Make top left/right corner pieces with large border thickness pixel
perfect.

Fixes: issue #988
src/theme.c

index e7ed3e891007a2259a562e8454f061be3960b2c9..719cf06deacab024be658f81f4abc624ee77e812 100644 (file)
@@ -454,7 +454,21 @@ rounded_rect(struct rounded_corner_ctx *ctx)
        cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
        cairo_paint(cairo);
 
-       /* fill */
+       /*
+        * Create outline path and fill. Illustration of top-left corner buffer:
+        *
+        *          _,,ooO"""""""""+
+        *        ,oO"'   ^        |
+        *      ,o"       |        |
+        *     o"         |r       |
+        *    o'          |        |
+        *    O     r     v        |
+        *    O<--------->+        |
+        *    O                    |
+        *    O                    |
+        *    O                    |
+        *    +--------------------+
+        */
        cairo_set_line_width(cairo, 0.0);
        cairo_new_sub_path(cairo);
        switch (ctx->corner) {
@@ -479,28 +493,102 @@ rounded_rect(struct rounded_corner_ctx *ctx)
        cairo_fill_preserve(cairo);
        cairo_stroke(cairo);
 
-       /* border */
-       cairo_set_line_cap(cairo, CAIRO_LINE_CAP_ROUND);
+       /*
+        * Stroke horizontal and vertical borders, shown by Xs and Ys
+        * respectively in the figure below:
+        *
+        *          _,,ooO"XXXXXXXXX
+        *        ,oO"'            |
+        *      ,o"                |
+        *     o"                  |
+        *    o'                   |
+        *    O                    |
+        *    Y                    |
+        *    Y                    |
+        *    Y                    |
+        *    Y                    |
+        *    Y--------------------+
+        */
+       cairo_set_line_cap(cairo, CAIRO_LINE_CAP_BUTT);
        set_cairo_color(cairo, ctx->border_color);
        cairo_set_line_width(cairo, ctx->line_width);
        double half_line_width = ctx->line_width / 2.0;
        switch (ctx->corner) {
        case LAB_CORNER_TOP_LEFT:
                cairo_move_to(cairo, half_line_width, h);
-               cairo_line_to(cairo, half_line_width, r + half_line_width);
-               cairo_arc(cairo, r, r, r - half_line_width, 180 * deg, 270 * deg);
+               cairo_line_to(cairo, half_line_width, r);
+               cairo_move_to(cairo, r, half_line_width);
                cairo_line_to(cairo, w, half_line_width);
                break;
        case LAB_CORNER_TOP_RIGHT:
                cairo_move_to(cairo, 0, half_line_width);
                cairo_line_to(cairo, w - r, half_line_width);
-               cairo_arc(cairo, w - r, r, r - half_line_width, -90 * deg, 0 * deg);
+               cairo_move_to(cairo, w - half_line_width, r);
                cairo_line_to(cairo, w - half_line_width, h);
                break;
        default:
                wlr_log(WLR_ERROR, "unknown corner type");
        }
        cairo_stroke(cairo);
+
+       /*
+        * If radius==0 the borders stroked above go right up to (and including)
+        * the corners, so there is not need to do any more.
+        */
+       if (!r) {
+               goto out;
+       }
+
+       /*
+        * Stroke the arc section of the border of the corner piece.
+        *
+        * Note: This figure is drawn at a more zoomed in scale compared with
+        * those above.
+        *
+        *                 ,,ooooO""  ^
+        *            ,ooo""'      |  |
+        *         ,oOO"           |  | line-thickness
+        *       ,OO"              |  |
+        *     ,OO"         _,,ooO""  v
+        *    ,O"         ,oO"'
+        *   ,O'        ,o"
+        *  ,O'        o"
+        *  o'        o'
+        *  O         O
+        *  O---------O            +
+        *       <----------------->
+        *          radius
+        *
+        * We handle the edge-case where line-thickness > radius by merely
+        * setting line-thickness = radius and in effect drawing a quadrant of a
+        * circle. In this case the X and Y borders butt up against the arc and
+        * overlap each other (as their line-thickessnes are greater than the
+        * linethickness of the arc). As a result, there is no inner rounded
+        * corners.
+        *
+        * So, in order to have inner rounded corners cornerRadius should be
+        * greater than border.width.
+        *
+        * Also, see diagrams in https://github.com/labwc/labwc/pull/990
+        */
+       double line_width = MIN(ctx->line_width, r);
+       cairo_set_line_width(cairo, line_width);
+       half_line_width = line_width / 2.0;
+       switch (ctx->corner) {
+       case LAB_CORNER_TOP_LEFT:
+               cairo_move_to(cairo, half_line_width, r);
+               cairo_arc(cairo, r, r, r - half_line_width, 180 * deg, 270 * deg);
+               break;
+       case LAB_CORNER_TOP_RIGHT:
+               cairo_move_to(cairo, w - r, half_line_width);
+               cairo_arc(cairo, w - r, r, r - half_line_width, -90 * deg, 0 * deg);
+               break;
+       default:
+               break;
+       }
+       cairo_stroke(cairo);
+
+out:
        cairo_surface_flush(surf);
 
        return buffer;