[gnome-shell] st-theme-node: Support non-uniform border widths



commit cb5c18c7833ee5e927cc3c6bd309a7ba828edd0d
Author: Florian Müllner <fmuellner gnome org>
Date:   Tue Nov 23 15:10:57 2010 +0100

    st-theme-node: Support non-uniform border widths
    
    While non-uniform border widths were parsed correctly, an arbitrary
    side's width was picked when painting, so that each border ended up
    with the same width and the widths specified in CSS were ignored.
    
    At least for sides between non-rounded corners, using a different
    border width can be reasonable, for instance at screen edges.
    
    Different border widths around rounded corners are kind of crack,
    but then it would be lame not to support it ...
    
    https://bugzilla.gnome.org/show_bug.cgi?id=607500

 src/st/st-theme-node-drawing.c    |  381 ++++++++++++++++++++++++-------------
 tests/Makefile.am                 |    1 +
 tests/interactive/border-width.js |   60 ++++++
 3 files changed, 310 insertions(+), 132 deletions(-)
---
diff --git a/src/st/st-theme-node-drawing.c b/src/st/st-theme-node-drawing.c
index 4a17ac2..6e71c98 100644
--- a/src/st/st-theme-node-drawing.c
+++ b/src/st/st-theme-node-drawing.c
@@ -49,6 +49,22 @@ typedef struct {
   guint          border_width_2;
 } StCornerSpec;
 
+static void
+elliptical_arc (cairo_t *cr,
+                double   x_center,
+                double   y_center,
+                double   x_radius,
+                double   y_radius,
+                double   angle1,
+                double   angle2)
+{
+  cairo_save (cr);
+  cairo_translate (cr, x_center, y_center);
+  cairo_scale (cr, x_radius, y_radius);
+  cairo_arc (cr, 0, 0, 1.0, angle1, angle2);
+  cairo_restore (cr);
+}
+
 static CoglHandle
 create_corner_material (StCornerSpec *corner)
 {
@@ -73,13 +89,11 @@ create_corner_material (StCornerSpec *corner)
   cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
   cairo_scale (cr, size, size);
 
-  /* TODO support nonuniform border widths */
-
-  if (corner->border_width_1 < corner->radius)
+  if (max_border_width <= corner->radius)
     {
-      double internal_radius = 0.5 * (1.0 - (double) corner->border_width_1 / corner->radius);
+      double x_radius, y_radius;
 
-      if (corner->border_width_1 != 0)
+      if (max_border_width != 0)
         {
           cairo_set_source_rgba (cr,
                                  corner->border_color_1.red / 255.,
@@ -96,14 +110,41 @@ create_corner_material (StCornerSpec *corner)
                              corner->color.green / 255.,
                              corner->color.blue / 255.,
                              corner->color.alpha / 255.);
-      cairo_arc (cr, 0.5, 0.5, internal_radius, 0, 2 * M_PI);
+
+      x_radius = 0.5 * (1.0 - (double) corner->border_width_2 / corner->radius);
+      y_radius = 0.5 * (1.0 - (double) corner->border_width_1 / corner->radius);
+
+      /* TOPRIGHT */
+      elliptical_arc (cr,
+                      0.5, 0.5,
+                      x_radius, y_radius,
+                      3 * M_PI / 2, 2 * M_PI);
+
+      /* BOTTOMRIGHT */
+      elliptical_arc (cr,
+                      0.5, 0.5,
+                      x_radius, y_radius,
+                      0, M_PI / 2);
+
+      /* TOPLEFT */
+      elliptical_arc (cr,
+                      0.5, 0.5,
+                      x_radius, y_radius,
+                      M_PI, 3 * M_PI / 2);
+
+      /* BOTTOMLEFT */
+      elliptical_arc (cr,
+                      0.5, 0.5,
+                      x_radius, y_radius,
+                      M_PI / 2, M_PI);
+
       cairo_fill (cr);
     }
   else
     {
       double radius;
 
-      radius = (gdouble)corner->radius / corner->border_width_1;
+      radius = (gdouble)corner->radius / max_border_width;
 
       cairo_set_source_rgba (cr,
                              corner->border_color_1.red / 255.,
@@ -216,6 +257,41 @@ over (const ClutterColor *source,
   unpremultiply (result);
 }
 
+static void
+st_theme_node_get_corner_border_widths (StThemeNode *node,
+                                        StCorner     corner_id,
+                                        guint       *border_width_1,
+                                        guint       *border_width_2)
+{
+  switch (corner_id)
+    {
+      case ST_CORNER_TOPLEFT:
+        if (border_width_1)
+            *border_width_1 = node->border_width[ST_SIDE_TOP];
+        if (border_width_2)
+            *border_width_2 = node->border_width[ST_SIDE_LEFT];
+        break;
+      case ST_CORNER_TOPRIGHT:
+        if (border_width_1)
+            *border_width_1 = node->border_width[ST_SIDE_TOP];
+        if (border_width_2)
+            *border_width_2 = node->border_width[ST_SIDE_RIGHT];
+        break;
+      case ST_CORNER_BOTTOMRIGHT:
+        if (border_width_1)
+            *border_width_1 = node->border_width[ST_SIDE_BOTTOM];
+        if (border_width_2)
+            *border_width_2 = node->border_width[ST_SIDE_RIGHT];
+        break;
+      case ST_CORNER_BOTTOMLEFT:
+        if (border_width_1)
+            *border_width_1 = node->border_width[ST_SIDE_BOTTOM];
+        if (border_width_2)
+            *border_width_2 = node->border_width[ST_SIDE_LEFT];
+        break;
+    }
+}
+
 static CoglHandle
 st_theme_node_lookup_corner (StThemeNode    *node,
                              StCorner        corner_id)
@@ -233,30 +309,25 @@ st_theme_node_lookup_corner (StThemeNode    *node,
 
   corner.radius = node->border_radius[corner_id];
   corner.color = node->background_color;
+  st_theme_node_get_corner_border_widths (node, corner_id,
+                                          &corner.border_width_1,
+                                          &corner.border_width_2);
 
   switch (corner_id)
     {
       case ST_CORNER_TOPLEFT:
-        corner.border_width_1 = node->border_width[ST_SIDE_TOP];
-        corner.border_width_2 = node->border_width[ST_SIDE_LEFT];
         over (&node->border_color[ST_SIDE_TOP], &corner.color, &corner.border_color_1);
         over (&node->border_color[ST_SIDE_LEFT], &corner.color, &corner.border_color_2);
         break;
       case ST_CORNER_TOPRIGHT:
-        corner.border_width_1 = node->border_width[ST_SIDE_TOP];
-        corner.border_width_2 = node->border_width[ST_SIDE_RIGHT];
         over (&node->border_color[ST_SIDE_TOP], &corner.color, &corner.border_color_1);
         over (&node->border_color[ST_SIDE_RIGHT], &corner.color, &corner.border_color_2);
         break;
       case ST_CORNER_BOTTOMRIGHT:
-        corner.border_width_1 = node->border_width[ST_SIDE_BOTTOM];
-        corner.border_width_2 = node->border_width[ST_SIDE_RIGHT];
         over (&node->border_color[ST_SIDE_BOTTOM], &corner.color, &corner.border_color_1);
         over (&node->border_color[ST_SIDE_RIGHT], &corner.color, &corner.border_color_2);
         break;
       case ST_CORNER_BOTTOMLEFT:
-        corner.border_width_1 = node->border_width[ST_SIDE_BOTTOM];
-        corner.border_width_2 = node->border_width[ST_SIDE_LEFT];
         over (&node->border_color[ST_SIDE_BOTTOM], &corner.color, &corner.border_color_1);
         over (&node->border_color[ST_SIDE_LEFT], &corner.color, &corner.border_color_2);
         break;
@@ -346,28 +417,14 @@ get_background_position (StThemeNode             *self,
 }
 
 /* Use of this function marks code which doesn't support
- * non-uniform widths and/or colors.
+ * non-uniform colors.
  */
-static gboolean
-get_arbitrary_border (StThemeNode   *node,
-                      int           *width,
-                      ClutterColor  *color)
+static void
+get_arbitrary_border_color (StThemeNode   *node,
+                            ClutterColor  *color)
 {
-  int w;
-
-  w = st_theme_node_get_border_width (node, ST_SIDE_TOP);
-  if (w > 0)
-    {
-      if (width)
-        *width = w;
-      if (color)
-        st_theme_node_get_border_color (node, ST_SIDE_TOP, color);
-      return TRUE;
-    }
-
-  if (width)
-    *width = 0;
-  return FALSE;
+  if (color)
+    st_theme_node_get_border_color (node, ST_SIDE_TOP, color);
 }
 
 static CoglHandle
@@ -379,7 +436,7 @@ st_theme_node_render_gradient (StThemeNode *node)
   cairo_surface_t *surface;
   cairo_pattern_t *pattern;
   ClutterColor border_color;
-  int border_width;
+  int border_width[4];
   guint rowstride;
   guchar *data;
 
@@ -392,11 +449,15 @@ st_theme_node_render_gradient (StThemeNode *node)
                                                  rowstride);
   cr = cairo_create (surface);
 
-  /* TODO - support non-uniform border colors and widths */
-  get_arbitrary_border (node, &border_width, &border_color);
+  /* TODO - support non-uniform border colors */
+  get_arbitrary_border_color (node, &border_color);
 
   for (i = 0; i < 4; i++)
-    radius[i] = st_theme_node_get_border_radius (node, i);
+    {
+      border_width[i] = st_theme_node_get_border_width (node, i);
+
+      radius[i] = st_theme_node_get_border_radius (node, i);
+    }
 
   if (node->background_gradient_type == ST_GRADIENT_VERTICAL)
     pattern = cairo_pattern_create_linear (0, 0, 0, node->alloc_height);
@@ -437,13 +498,13 @@ st_theme_node_render_gradient (StThemeNode *node)
                radius[ST_CORNER_TOPRIGHT],
                radius[ST_CORNER_TOPRIGHT], 3 * M_PI / 2, 2 * M_PI);
   cairo_line_to (cr, node->alloc_width, node->alloc_height - radius[ST_CORNER_BOTTOMRIGHT]);
-  if (radius[ST_CORNER_BOTTOMRIGHT])
+  if (radius[ST_CORNER_BOTTOMRIGHT] > 0)
     cairo_arc (cr,
                node->alloc_width - radius[ST_CORNER_BOTTOMRIGHT],
                node->alloc_height - radius[ST_CORNER_BOTTOMRIGHT],
                radius[ST_CORNER_BOTTOMRIGHT], 0, M_PI / 2);
   cairo_line_to (cr, radius[ST_CORNER_BOTTOMLEFT], node->alloc_height);
-  if (radius[ST_CORNER_BOTTOMLEFT])
+  if (radius[ST_CORNER_BOTTOMLEFT] > 0)
     cairo_arc (cr,
                radius[ST_CORNER_BOTTOMLEFT],
                node->alloc_height - radius[ST_CORNER_BOTTOMLEFT],
@@ -456,7 +517,10 @@ st_theme_node_render_gradient (StThemeNode *node)
    * otherwise the outline shape is filled with the background
    * gradient directly
    */
-  if (border_width > 0)
+  if (border_width[ST_SIDE_TOP] > 0 ||
+      border_width[ST_SIDE_RIGHT] > 0 ||
+      border_width[ST_SIDE_BOTTOM] > 0 ||
+      border_width[ST_SIDE_LEFT] > 0)
     {
       cairo_set_source_rgba (cr,
                              border_color.red / 255.,
@@ -465,47 +529,70 @@ st_theme_node_render_gradient (StThemeNode *node)
                              border_color.alpha / 255.);
       cairo_fill (cr);
 
-      if (radius[ST_CORNER_TOPLEFT] > border_width)
-        cairo_arc (cr,
-                   radius[ST_CORNER_TOPLEFT],
-                   radius[ST_CORNER_TOPLEFT],
-                   radius[ST_CORNER_TOPLEFT] - border_width,
-                   M_PI, 3 * M_PI / 2);
+      if (radius[ST_CORNER_TOPLEFT] > MAX(border_width[ST_SIDE_TOP],
+                                          border_width[ST_SIDE_LEFT]))
+        elliptical_arc (cr,
+                        radius[ST_CORNER_TOPLEFT],
+                        radius[ST_CORNER_TOPLEFT],
+                        radius[ST_CORNER_TOPLEFT] - border_width[ST_SIDE_LEFT],
+                        radius[ST_CORNER_TOPLEFT] - border_width[ST_SIDE_TOP],
+                        M_PI, 3 * M_PI / 2);
       else
-        cairo_move_to (cr, border_width, border_width);
+        cairo_move_to (cr,
+                       border_width[ST_SIDE_LEFT],
+                       border_width[ST_SIDE_TOP]);
 
       cairo_line_to (cr,
-                     node->alloc_width - MAX(radius[ST_CORNER_TOPRIGHT], border_width),
-                     border_width);
-
-      if (radius[ST_CORNER_TOPRIGHT] > border_width)
-        cairo_arc (cr,
-                   node->alloc_width - radius[ST_CORNER_TOPRIGHT],
-                   radius[ST_CORNER_TOPRIGHT],
-                   radius[ST_CORNER_TOPRIGHT] - border_width,
-                   3 * M_PI / 2, 2 * M_PI);
+                     node->alloc_width - MAX(radius[ST_CORNER_TOPRIGHT], border_width[ST_SIDE_RIGHT]),
+                     border_width[ST_SIDE_TOP]);
+
+      if (radius[ST_CORNER_TOPRIGHT] > MAX(border_width[ST_SIDE_TOP],
+                                           border_width[ST_SIDE_RIGHT]))
+        elliptical_arc (cr,
+                        node->alloc_width - radius[ST_CORNER_TOPRIGHT],
+                        radius[ST_CORNER_TOPRIGHT],
+                        radius[ST_CORNER_TOPRIGHT] - border_width[ST_SIDE_RIGHT],
+                        radius[ST_CORNER_TOPRIGHT] - border_width[ST_SIDE_TOP],
+                        3 * M_PI / 2, 2 * M_PI);
+      else
+        cairo_line_to (cr,
+                       node->alloc_width - border_width[ST_SIDE_RIGHT],
+                       border_width[ST_SIDE_TOP]);
 
       cairo_line_to (cr,
-                     node->alloc_width - border_width,
-                     node->alloc_height - MAX(radius[ST_CORNER_BOTTOMRIGHT], border_width));
-
-      if (radius[ST_CORNER_BOTTOMRIGHT] > border_width)
-        cairo_arc (cr,
-                   node->alloc_width - radius[ST_CORNER_BOTTOMRIGHT],
-                   node->alloc_height - radius[ST_CORNER_BOTTOMRIGHT],
-                   radius[ST_CORNER_BOTTOMRIGHT] - border_width,
-                   0, M_PI / 2);
+                     node->alloc_width - border_width[ST_SIDE_RIGHT],
+                     node->alloc_height - MAX(radius[ST_CORNER_BOTTOMRIGHT], border_width[ST_SIDE_BOTTOM]));
+
+      if (radius[ST_CORNER_BOTTOMRIGHT] > MAX(border_width[ST_SIDE_BOTTOM],
+                                              border_width[ST_SIDE_RIGHT]))
+        elliptical_arc (cr,
+                        node->alloc_width - radius[ST_CORNER_BOTTOMRIGHT],
+                        node->alloc_height - radius[ST_CORNER_BOTTOMRIGHT],
+                        radius[ST_CORNER_BOTTOMRIGHT] - border_width[ST_SIDE_RIGHT],
+                        radius[ST_CORNER_BOTTOMRIGHT] - border_width[ST_SIDE_BOTTOM],
+                        0, M_PI / 2);
+      else
+        cairo_line_to (cr,
+                       node->alloc_width - border_width[ST_SIDE_RIGHT],
+                       node->alloc_height - border_width[ST_SIDE_BOTTOM]);
 
       cairo_line_to (cr,
-                     MAX(radius[ST_CORNER_BOTTOMLEFT], border_width),
-                     node->alloc_height - border_width);
-
-      if (radius[ST_CORNER_BOTTOMLEFT] > border_width)
-        cairo_arc (cr,
-                   radius[ST_CORNER_BOTTOMLEFT],
-                   node->alloc_height - radius[ST_CORNER_BOTTOMLEFT],
-                   radius[ST_CORNER_BOTTOMLEFT] - border_width,
-                   M_PI / 2, M_PI);
+                     MAX(radius[ST_CORNER_BOTTOMLEFT], border_width[ST_SIDE_LEFT]),
+                     node->alloc_height - border_width[ST_SIDE_BOTTOM]);
+
+      if (radius[ST_CORNER_BOTTOMLEFT] > MAX(border_width[ST_SIDE_BOTTOM],
+                                             border_width[ST_SIDE_LEFT]))
+        elliptical_arc (cr,
+                        radius[ST_CORNER_BOTTOMLEFT],
+                        node->alloc_height - radius[ST_CORNER_BOTTOMLEFT],
+                        radius[ST_CORNER_BOTTOMLEFT] - border_width[ST_SIDE_LEFT],
+                        radius[ST_CORNER_BOTTOMLEFT] - border_width[ST_SIDE_BOTTOM],
+                        M_PI / 2, M_PI);
+      else
+        cairo_line_to (cr,
+                       border_width[ST_SIDE_LEFT],
+                       node->alloc_height - border_width[ST_SIDE_BOTTOM]);
+
       cairo_close_path (cr);
     }
 
@@ -706,27 +793,40 @@ st_theme_node_paint_borders (StThemeNode           *node,
 
 {
   float width, height;
-  int border_width;
+  int border_width[4];
   int max_border_radius = 0;
   int max_width_radius[4];
-  int corner_id;
+  int corner_id, side_id;
   ClutterColor border_color;
   guint8 alpha;
 
   width = box->x2 - box->x1;
   height = box->y2 - box->y1;
 
-  get_arbitrary_border (node, &border_width, &border_color);
+  /* TODO - support non-uniform border colors */
+  get_arbitrary_border_color (node, &border_color);
+
+  for (side_id = 0; side_id < 4; side_id++)
+    border_width[side_id] = st_theme_node_get_border_width(node, side_id);
+
   for (corner_id = 0; corner_id < 4; corner_id++)
     {
+      guint border_width_1, border_width_2;
+
+      st_theme_node_get_corner_border_widths (node, corner_id,
+                                              &border_width_1, &border_width_2);
+
       if (node->border_radius[corner_id] > max_border_radius)
         max_border_radius = node->border_radius[corner_id];
-      max_width_radius[corner_id] = MAX(border_width,
+      max_width_radius[corner_id] = MAX(MAX(border_width_1, border_width_2),
                                         node->border_radius[corner_id]);
     }
 
   /* borders */
-  if (border_width > 0)
+  if (border_width[ST_SIDE_TOP] > 0 ||
+      border_width[ST_SIDE_RIGHT] > 0 ||
+      border_width[ST_SIDE_BOTTOM] > 0 ||
+      border_width[ST_SIDE_LEFT] > 0)
     {
       ClutterColor effective_border;
       gboolean skip_corner_1, skip_corner_2;
@@ -749,18 +849,19 @@ st_theme_node_paint_borders (StThemeNode           *node,
           x1 = skip_corner_1 ? max_width_radius[ST_CORNER_TOPLEFT] : 0;
           y1 = 0;
           x2 = skip_corner_2 ? width - max_width_radius[ST_CORNER_TOPRIGHT] : width;
-          y2 = border_width;
+          y2 = border_width[ST_SIDE_TOP];
           cogl_rectangle (x1, y1, x2, y2);
 
           /* EAST */
           skip_corner_1 = node->border_radius[ST_CORNER_TOPRIGHT] > 0;
           skip_corner_2 = node->border_radius[ST_CORNER_BOTTOMRIGHT] > 0;
 
-          x1 = width - border_width;
-          y1 = skip_corner_1 ? max_width_radius[ST_CORNER_TOPRIGHT] : border_width;
+          x1 = width - border_width[ST_SIDE_RIGHT];
+          y1 = skip_corner_1 ? max_width_radius[ST_CORNER_TOPRIGHT]
+                             : border_width[ST_SIDE_TOP];
           x2 = width;
           y2 = skip_corner_2 ? height - max_width_radius[ST_CORNER_BOTTOMRIGHT]
-                             : height - border_width;
+                             : height - border_width[ST_SIDE_BOTTOM];
           cogl_rectangle (x1, y1, x2, y2);
 
           /* SOUTH */
@@ -768,7 +869,7 @@ st_theme_node_paint_borders (StThemeNode           *node,
           skip_corner_2 = node->border_radius[ST_CORNER_BOTTOMRIGHT] > 0;
 
           x1 = skip_corner_1 ? max_width_radius[ST_CORNER_BOTTOMLEFT] : 0;
-          y1 = height - border_width;
+          y1 = height - border_width[ST_SIDE_BOTTOM];
           x2 = skip_corner_2 ? width - max_width_radius[ST_CORNER_BOTTOMRIGHT]
                              : width;
           y2 = height;
@@ -779,10 +880,11 @@ st_theme_node_paint_borders (StThemeNode           *node,
           skip_corner_2 = node->border_radius[ST_CORNER_BOTTOMLEFT] > 0;
 
           x1 = 0;
-          y1 = skip_corner_1 ? max_width_radius[ST_CORNER_TOPLEFT] : border_width;
-          x2 = border_width;
+          y1 = skip_corner_1 ? max_width_radius[ST_CORNER_TOPLEFT]
+                             : border_width[ST_SIDE_TOP];
+          x2 = border_width[ST_SIDE_LEFT];
           y2 = skip_corner_2 ? height - max_width_radius[ST_CORNER_BOTTOMLEFT]
-                             : height - border_width;
+                             : height - border_width[ST_SIDE_BOTTOM];
           cogl_rectangle (x1, y1, x2, y2);
         }
     }
@@ -853,82 +955,99 @@ st_theme_node_paint_borders (StThemeNode           *node,
           switch (corner_id)
             {
               case ST_CORNER_TOPLEFT:
-                verts[0] = border_width;
-                verts[1] = max_width_radius[ST_CORNER_TOPLEFT];
+                verts[0] = border_width[ST_SIDE_LEFT];
+                verts[1] = MAX(node->border_radius[corner_id],
+                               border_width[ST_SIDE_TOP]);
                 verts[2] = max_border_radius;
                 verts[3] = max_border_radius;
                 if (n_rects == 2)
                   {
-                    verts[4] = max_width_radius[ST_CORNER_TOPLEFT];
-                    verts[5] = border_width;
+                    verts[4] = MAX(node->border_radius[corner_id],
+                                   border_width[ST_SIDE_LEFT]);
+                    verts[5] = border_width[ST_SIDE_TOP];
                     verts[6] = max_border_radius;
-                    verts[7] = max_width_radius[ST_CORNER_TOPLEFT];
+                    verts[7] = MAX(node->border_radius[corner_id],
+                                   border_width[ST_SIDE_TOP]);
                   }
                 break;
               case ST_CORNER_TOPRIGHT:
                 verts[0] = width - max_border_radius;
-                verts[1] = max_width_radius[ST_CORNER_TOPRIGHT];
-                verts[2] = width - border_width;
+                verts[1] = MAX(node->border_radius[corner_id],
+                               border_width[ST_SIDE_TOP]);
+                verts[2] = width - border_width[ST_SIDE_RIGHT];
                 verts[3] = max_border_radius;
                 if (n_rects == 2)
                   {
                     verts[4] = width - max_border_radius;
-                    verts[5] = border_width;
-                    verts[6] = width - max_width_radius[ST_CORNER_TOPRIGHT];
-                    verts[7] = max_width_radius[ST_CORNER_TOPRIGHT];
+                    verts[5] = border_width[ST_SIDE_TOP];
+                    verts[6] = width - MAX(node->border_radius[corner_id],
+                                           border_width[ST_SIDE_RIGHT]);
+                    verts[7] = MAX(node->border_radius[corner_id],
+                                   border_width[ST_SIDE_TOP]);
                   }
                 break;
               case ST_CORNER_BOTTOMRIGHT:
                 verts[0] = width - max_border_radius;
                 verts[1] = height - max_border_radius;
-                verts[2] = width - border_width;
-                verts[3] = height - max_width_radius[ST_CORNER_BOTTOMRIGHT];
+                verts[2] = width - border_width[ST_SIDE_RIGHT];
+                verts[3] = height - MAX(node->border_radius[corner_id],
+                                        border_width[ST_SIDE_BOTTOM]);
                 if (n_rects == 2)
                   {
                     verts[4] = width - max_border_radius;
-                    verts[5] = height - max_width_radius[ST_CORNER_BOTTOMRIGHT];
-                    verts[6] = width - max_width_radius[ST_CORNER_BOTTOMRIGHT];
-                    verts[7] = height - border_width;
+                    verts[5] = height - MAX(node->border_radius[corner_id],
+                                            border_width[ST_SIDE_BOTTOM]);
+                    verts[6] = width - MAX(node->border_radius[corner_id],
+                                           border_width[ST_SIDE_RIGHT]);
+                    verts[7] = height - border_width[ST_SIDE_BOTTOM];
                   }
                 break;
               case ST_CORNER_BOTTOMLEFT:
-                verts[0] = border_width;
+                verts[0] = border_width[ST_SIDE_LEFT];
                 verts[1] = height - max_border_radius;
                 verts[2] = max_border_radius;
-                verts[3] = height - max_width_radius[ST_CORNER_BOTTOMLEFT];
+                verts[3] = height - MAX(node->border_radius[corner_id],
+                                        border_width[ST_SIDE_BOTTOM]);
                 if (n_rects == 2)
                   {
-                    verts[4] = max_width_radius[ST_CORNER_BOTTOMLEFT];
-                    verts[5] = height - max_width_radius[ST_CORNER_BOTTOMLEFT];
+                    verts[4] = MAX(node->border_radius[corner_id],
+                                   border_width[ST_SIDE_LEFT]);
+                    verts[5] = height - MAX(node->border_radius[corner_id],
+                                            border_width[ST_SIDE_BOTTOM]);
                     verts[6] = max_border_radius;
-                    verts[7] = height - border_width;
+                    verts[7] = height - border_width[ST_SIDE_BOTTOM];
                   }
                 break;
             }
           cogl_rectangles (verts, n_rects);
         }
 
-      if (max_border_radius > border_width)
-        {
-          /* Once we've drawn the borders and corners, if the corners are bigger
-           * the the border width, the remaining area is shaped like
-           *
-           *  ########
-           * ##########
-           * ##########
-           *  ########
-           *
-           * We draw it in 3 pieces - first the top and bottom, then the main
-           * rectangle
-           */
-          cogl_rectangle (max_border_radius, border_width,
-                          width - max_border_radius, max_border_radius);
-          cogl_rectangle (max_border_radius, height - max_border_radius,
-                          width - max_border_radius, height - border_width);
-        }
-
-      cogl_rectangle (border_width, MAX(border_width, max_border_radius),
-                      width - border_width, height - MAX(border_width, max_border_radius));
+      /* Once we've drawn the borders and corners, if the corners are bigger
+       * then the border width, the remaining area is shaped like
+       *
+       *  ########
+       * ##########
+       * ##########
+       *  ########
+       *
+       * We draw it in at most 3 pieces - first the top and bottom if
+       * necessary, then the main rectangle
+       */
+      if (max_border_radius > border_width[ST_SIDE_TOP])
+        cogl_rectangle (MAX(max_border_radius, border_width[ST_SIDE_LEFT]),
+                        border_width[ST_SIDE_TOP],
+                        width - MAX(max_border_radius, border_width[ST_SIDE_RIGHT]),
+                        max_border_radius);
+      if (max_border_radius > border_width[ST_SIDE_BOTTOM])
+        cogl_rectangle (MAX(max_border_radius, border_width[ST_SIDE_LEFT]),
+                        height - max_border_radius,
+                        width - MAX(max_border_radius, border_width[ST_SIDE_RIGHT]),
+                        height - border_width[ST_SIDE_BOTTOM]);
+
+      cogl_rectangle (border_width[ST_SIDE_LEFT],
+                      MAX(border_width[ST_SIDE_TOP], max_border_radius),
+                      width - border_width[ST_SIDE_RIGHT],
+                      height - MAX(border_width[ST_SIDE_BOTTOM], max_border_radius));
     }
 }
 
@@ -1108,8 +1227,6 @@ st_theme_node_paint (StThemeNode           *node,
    *   zero width or a border image is being used.
    *
    * Deviations from the above as implemented here:
-   *  - Nonuniform border widths combined with a non-zero border radius result
-   *    in the border radius being ignored
    *  - The combination of border image and a non-zero border radius is
    *    not supported; the background color will be drawn with square
    *    corners.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 4c6b921..9fb38a2 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -4,6 +4,7 @@ EXTRA_DIST = run-test.sh.in
 TEST_JS =					\
 	interactive/borders.js			\
 	interactive/border-radius.js		\
+	interactive/border-width.js		\
 	interactive/box-layout.js		\
 	interactive/calendar.js			\
 	interactive/css-fonts.js		\
diff --git a/tests/interactive/border-width.js b/tests/interactive/border-width.js
new file mode 100644
index 0000000..60f7606
--- /dev/null
+++ b/tests/interactive/border-width.js
@@ -0,0 +1,60 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+const Clutter = imports.gi.Clutter;
+const St = imports.gi.St;
+
+const UI = imports.testcommon.ui;
+
+UI.init();
+let stage = Clutter.Stage.get_default();
+stage.width = 640;
+stage.height = 480;
+
+let vbox = new St.BoxLayout({ width: stage.width,
+                              height: stage.height,
+                              style: 'padding: 10px; background: #ffee88;'
+                            });
+stage.add_actor(vbox);
+
+let scroll = new St.ScrollView();
+vbox.add(scroll, { expand: true });
+
+let box = new St.BoxLayout({ vertical: true,
+                             style: 'spacing: 20px;' });
+scroll.add_actor(box);
+
+function addTestCase(borders, useGradient) {
+    let background;
+    if (useGradient)
+        background = 'background-gradient-direction: vertical;'
+                     + 'background-gradient-start: white;'
+                     + 'background-gradient-end: gray;';
+    else
+        background = 'background: white;';
+
+    let border_style = "border-top: " + borders[St.Side.TOP] + " solid black;\n" +
+                       "border-right: " + borders[St.Side.RIGHT] + " solid black;\n" +
+                       "border-bottom: " + borders[St.Side.BOTTOM] + " solid black;\n" +
+                       "border-left: " + borders[St.Side.LEFT] + " solid black;";
+    box.add(new St.Label({ text: border_style,
+                           style: border_style
+                                  + 'border-radius: 0px 5px 15px 25px;'
+                                  + 'padding: 5px;' + background }),
+                         { x_fill: false });
+}
+
+// uniform backgrounds
+addTestCase([" 0px", " 5px", "10px", "15px"], false);
+addTestCase([" 5px", "10px", "15px", " 0px"], false);
+addTestCase(["10px", "15px", " 0px", " 5px"], false);
+addTestCase(["15px", " 0px", " 5px", "10px"], false);
+
+// gradient backgrounds
+addTestCase([" 0px", " 5px", "10px", "15px"], true);
+addTestCase([" 5px", "10px", "15px", " 0px"], true);
+addTestCase(["10px", "15px", " 0px", " 5px"], true);
+addTestCase(["15px", " 0px", " 5px", "10px"], true);
+
+stage.show();
+Clutter.main();
+stage.destroy();



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]