[gnome-shell] Change handling of gradients



commit 7486c09fbbc55a247fe3c2675d763b40b5a37e0b
Author: Florian Müllner <fmuellner src gnome org>
Date:   Thu Jan 7 14:40:26 2010 +0100

    Change handling of gradients
    
    Some changes to the way we handle CSS gradients:
    
     * draw without padding, thus interpreting gradients as part of the
       background rather than as content
    
     * clip to (rounded) border area
    
     * draw the border along the gradient instead of trying to align the
       gradient layer with the background/border layer
    
     * use the border_image actor instead of the background_image one
    
    https://bugzilla.gnome.org/show_bug.cgi?id=606257

 src/st/st-widget.c |  231 ++++++++++++++++++++++++++++++----------------------
 1 files changed, 135 insertions(+), 96 deletions(-)
---
diff --git a/src/st/st-widget.c b/src/st/st-widget.c
index 023be7d..e97d7cf 100644
--- a/src/st/st-widget.c
+++ b/src/st/st-widget.c
@@ -30,6 +30,7 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <math.h>
 
 #include <clutter/clutter.h>
 
@@ -61,6 +62,9 @@ struct _StWidgetPrivate
   ClutterActor *background_image_shadow;
   ClutterColor  bg_color;
 
+  guint         border_width;
+  ClutterColor  border_color;
+
   StGradientType bg_gradient_type;
   ClutterColor  bg_gradient_end;
 
@@ -302,7 +306,7 @@ st_widget_allocate (ClutterActor          *actor,
 
 
 
-  if (priv->border_image)
+  if (priv->border_image && priv->bg_gradient_type == ST_GRADIENT_NONE)
     {
       ClutterActorBox frame_box = {
         0,
@@ -315,8 +319,27 @@ st_widget_allocate (ClutterActor          *actor,
                               &frame_box,
                               flags);
     }
+  else if (priv->bg_gradient_type != ST_GRADIENT_NONE)
+    {
+      float width, height;
+      ClutterActorBox frame_box;
 
-  if (priv->background_image && priv->bg_gradient_type == ST_GRADIENT_NONE)
+      width = box->x2 - box->x1;
+      height = box->y2 - box->y1;
+      frame_box.x1 = frame_box.y1 = 0;
+      frame_box.x2 = width;
+      frame_box.y2 = height;
+
+      if (width > 0 && height > 0)
+        clutter_cairo_texture_set_surface_size (CLUTTER_CAIRO_TEXTURE (priv->border_image),
+                                                width, height);
+      st_widget_redraw_gradient ((StWidget*) actor);
+      clutter_actor_allocate (CLUTTER_ACTOR (priv->border_image),
+                              &frame_box,
+                              flags);
+    }
+
+  if (priv->background_image)
     {
       ClutterActorBox frame_box = {
         0, 0, box->x2 - box->x1, box->y2 - box->y1
@@ -393,27 +416,6 @@ st_widget_allocate (ClutterActor          *actor,
                               &frame_box,
                               flags);
     }
-  else if (priv->bg_gradient_type != ST_GRADIENT_NONE)
-    {
-      float width, height;
-      ClutterActorBox frame_box, content_box;
-
-      width = box->x2 - box->x1;
-      height = box->y2 - box->y1;
-      frame_box.x1 = frame_box.y1 = 0;
-      frame_box.x2 = width;
-      frame_box.y2 = height;
-
-      st_theme_node_get_content_box (theme_node, &frame_box, &content_box);
-
-      if (width > 0 && height > 0)
-        clutter_cairo_texture_set_surface_size (CLUTTER_CAIRO_TEXTURE (priv->background_image),
-                                                width, height);
-      st_widget_redraw_gradient ((StWidget*) actor);
-      clutter_actor_allocate (CLUTTER_ACTOR (priv->background_image),
-                              &content_box,
-                              flags);
-    }
 }
 
 static void
@@ -598,52 +600,41 @@ st_widget_unmap (ClutterActor *actor)
 }
 
 static void
-draw_vertical_gradient (ClutterCairoTexture  *texture,
-                        ClutterColor         *start,
-                        ClutterColor         *end)
+st_widget_redraw_gradient (StWidget  *widget)
 {
+  ClutterCairoTexture *texture;
+  ClutterColor *start, *end;
+  StWidgetPrivate *priv;
   guint width, height;
+  guint radius[4], i;
   cairo_t *cr;
   cairo_pattern_t *pattern;
+  gboolean round_border = FALSE;
 
-  clutter_cairo_texture_get_surface_size (texture, &width, &height);
-  clutter_cairo_texture_clear (texture);
-  cr = clutter_cairo_texture_create (texture);
-
-  pattern = cairo_pattern_create_linear (0, 0, 0, height);
-  cairo_pattern_add_color_stop_rgba (pattern, 0,
-                                     start->red / 255.,
-                                     start->green / 255.,
-                                     start->blue / 255.,
-                                     start->alpha / 255.);
-  cairo_pattern_add_color_stop_rgba (pattern, 1,
-                                     end->red / 255.,
-                                     end->green / 255.,
-                                     end->blue / 255.,
-                                     end->alpha / 255.);
+  if (widget->priv->bg_gradient_type == ST_GRADIENT_NONE)
+    return;
 
-  cairo_rectangle (cr, 0, 0, width, height);
-  cairo_set_source (cr, pattern);
-  cairo_fill (cr);
+  texture = CLUTTER_CAIRO_TEXTURE (widget->priv->border_image);
+  priv  = widget->priv;
+  start = &widget->priv->bg_color;
+  end   = &widget->priv->bg_gradient_end;
 
-  cairo_pattern_destroy (pattern);
-  cairo_destroy (cr);
-}
-
-static void
-draw_horizontal_gradient (ClutterCairoTexture  *texture,
-                          ClutterColor         *start,
-                          ClutterColor         *end)
-{
-  guint width, height;
-  cairo_t *cr;
-  cairo_pattern_t *pattern;
+  for (i = 0; i < 4; i++)
+    {
+      radius[i] = st_theme_node_get_border_radius (priv->theme_node, i);
+      if (radius[i] > 0)
+        round_border = TRUE;
+    }
 
   clutter_cairo_texture_get_surface_size (texture, &width, &height);
   clutter_cairo_texture_clear (texture);
   cr = clutter_cairo_texture_create (texture);
 
-  pattern = cairo_pattern_create_linear (0, 0, width, 0);
+  if (priv->bg_gradient_type == ST_GRADIENT_VERTICAL)
+    pattern = cairo_pattern_create_linear (0, 0, 0, height);
+  else
+    pattern = cairo_pattern_create_linear (0, 0, width, 0);
+
   cairo_pattern_add_color_stop_rgba (pattern, 0,
                                      start->red / 255.,
                                      start->green / 255.,
@@ -654,7 +645,64 @@ draw_horizontal_gradient (ClutterCairoTexture  *texture,
                                      end->green / 255.,
                                      end->blue / 255.,
                                      end->alpha / 255.);
-  cairo_rectangle (cr, 0, 0, width, height);
+
+  if (round_border)
+    {
+      if (radius[ST_CORNER_TOPLEFT] > 0)
+        cairo_arc (cr,
+                   radius[ST_CORNER_TOPLEFT],
+                   radius[ST_CORNER_TOPLEFT],
+                   radius[ST_CORNER_TOPLEFT], M_PI, 3 * M_PI / 2);
+      else
+        cairo_move_to (cr, 0, 0);
+      cairo_line_to (cr, width - radius[ST_CORNER_TOPRIGHT], 0);
+      if (radius[ST_CORNER_TOPRIGHT] > 0)
+        cairo_arc (cr,
+                   width - radius[ST_CORNER_TOPRIGHT],
+                   radius[ST_CORNER_TOPRIGHT],
+                   radius[ST_CORNER_TOPRIGHT], 3 * M_PI / 2, 2 * M_PI);
+      cairo_line_to (cr, width, height - radius[ST_CORNER_BOTTOMRIGHT]);
+      if (radius[ST_CORNER_BOTTOMRIGHT])
+        cairo_arc (cr,
+                   width - radius[ST_CORNER_BOTTOMRIGHT],
+                   height - radius[ST_CORNER_BOTTOMRIGHT],
+                   radius[ST_CORNER_BOTTOMRIGHT], 0, M_PI / 2);
+      cairo_line_to (cr, radius[ST_CORNER_BOTTOMLEFT], height);
+      if (radius[ST_CORNER_BOTTOMLEFT])
+        cairo_arc (cr,
+                   radius[ST_CORNER_BOTTOMLEFT],
+                   height - radius[ST_CORNER_BOTTOMLEFT],
+                   radius[ST_CORNER_BOTTOMLEFT], M_PI / 2, M_PI);
+      cairo_close_path (cr);
+    }
+  else
+    cairo_rectangle (cr, 0, 0, width, height);
+
+  if (priv->border_width > 0)
+    {
+      guint8 opacity;
+      gdouble effective_alpha;
+      cairo_path_t *path;
+
+      path = cairo_copy_path (cr);
+      opacity = clutter_actor_get_paint_opacity (CLUTTER_ACTOR (widget));
+      effective_alpha = priv->border_color.alpha * opacity / (255. * 255.);
+
+      cairo_set_source_rgba (cr,
+                             priv->border_color.red / 255.,
+                             priv->border_color.green / 255.,
+                             priv->border_color.blue / 255.,
+                             effective_alpha);
+      cairo_fill (cr);
+
+      cairo_translate (cr, priv->border_width, priv->border_width);
+      cairo_scale (cr,
+                   (gdouble)(width - 2 * priv->border_width) / width,
+                   (gdouble)(height - 2 * priv->border_width) / height);
+      cairo_append_path (cr, path);
+      cairo_path_destroy (path);
+    }
+
   cairo_set_source (cr, pattern);
   cairo_fill (cr);
 
@@ -662,19 +710,6 @@ draw_horizontal_gradient (ClutterCairoTexture  *texture,
   cairo_destroy (cr);
 }
 
-static void
-st_widget_redraw_gradient (StWidget  *widget)
-{
-  if (widget->priv->bg_gradient_type == ST_GRADIENT_VERTICAL)
-    draw_vertical_gradient ((ClutterCairoTexture*) widget->priv->background_image,
-                            &widget->priv->bg_color,
-                            &widget->priv->bg_gradient_end);
-  else if (widget->priv->bg_gradient_type == ST_GRADIENT_HORIZONTAL)
-    draw_horizontal_gradient ((ClutterCairoTexture*) widget->priv->background_image,
-                              &widget->priv->bg_color,
-                              &widget->priv->bg_gradient_end);
-}
-
 static void notify_children_of_style_change (ClutterContainer *container);
 
 static void
@@ -709,9 +744,7 @@ st_widget_real_style_changed (StWidget *self)
   gboolean relayout_needed = FALSE;
   gboolean has_changed = FALSE;
   ClutterColor color;
-  guint border_width = 0;
   guint border_radius = 0;
-  ClutterColor border_color = { 0, };
   StGradientType gradient;
   ClutterColor gradient_end;
   StSide side;
@@ -779,9 +812,9 @@ st_widget_real_style_changed (StWidget *self)
    *     Background image
    *     Border color or border image
    *     Content
-   * - The background color and image extend to and are clipped by the
-   *   edge of the border area, so will be rounded if the border is rounded.
-   *   (CSS3 background-clip property modifies this)
+   * - The background color, gradient and image extend to and are clipped by
+   *   the edge of the border area, so will be rounded if the border is
+   *   rounded. (CSS3 background-clip property modifies this)
    * - The border image replaces what would normally be drawn by the border
    * - The border image is not clipped by a rounded border-radius
    * - The border radius rounds the background even if the border is
@@ -793,21 +826,24 @@ st_widget_real_style_changed (StWidget *self)
    *  - The combination of border image and a non-zero border radius is
    *    not supported; the background color will be drawn with square
    *    corners.
+   *  - The combination of border image and a background gradient is not
+   *    supported; the background will be drawn as a solid color
    *  - The background image is drawn above the border color or image,
    *    not below it.
    *  - We don't clip the background image to the (rounded) border area.
    *
-   * The first two allow us always draw with no more than single border_image
-   * and a single background image above it.
+   * The first three allow us to always draw with no more than a single
+   * border_image and a single background image above it.
    */
 
   /* Check whether all border widths are the same.  Also, acquire the
    * first nonzero border width as well as the border color.
    */
   uniform_border_width = TRUE;
-  border_width = st_theme_node_get_border_width (theme_node, ST_SIDE_TOP);
-  if (border_width > 0.5)
-    border_width = (int)(0.5 + border_width);
+  priv->border_width = st_theme_node_get_border_width (theme_node,
+                                                       ST_SIDE_TOP);
+  if (priv->border_width > 0.5)
+    priv->border_width = (int)(0.5 + priv->border_width);
   for (side = 0; side < 4; side++)
     {
       double width = st_theme_node_get_border_width (theme_node, side);
@@ -815,10 +851,11 @@ st_widget_real_style_changed (StWidget *self)
         width = (int)(0.5 + width);
       if (width > 0)
         {
-          border_width = width;
-          st_theme_node_get_border_color (theme_node, side, &border_color);
+          priv->border_width = width;
+          st_theme_node_get_border_color (theme_node,
+                                          side, &priv->border_color);
         }
-      if ((int)width != border_width)
+      if ((int)width != priv->border_width)
         {
           uniform_border_width = FALSE;
           break;
@@ -870,14 +907,25 @@ st_widget_real_style_changed (StWidget *self)
       has_changed = TRUE;
       relayout_needed = TRUE;
     }
+  else if (priv->bg_gradient_type != ST_GRADIENT_NONE)
+    {
+      priv->draw_border_internal = FALSE;
+      priv->draw_bg_color = FALSE;
+      texture = g_object_new (CLUTTER_TYPE_CAIRO_TEXTURE, NULL);
+      priv->border_image = CLUTTER_ACTOR (texture);
+      clutter_actor_set_parent (priv->border_image, CLUTTER_ACTOR (self));
+
+      has_changed = TRUE;
+      relayout_needed = TRUE;
+    }
   else if (border_radius > 0)
     {
       priv->draw_border_internal = FALSE;
       priv->draw_bg_color = FALSE;
       priv->border_image = g_object_new (BIG_TYPE_RECTANGLE,
 					 "color", &priv->bg_color,
-					 "border-width", border_width,
-					 "border-color", &border_color,
+					 "border-width", priv->border_width,
+					 "border-color", &priv->border_color,
 					 "corner-radius", border_radius,
 					 NULL);
 
@@ -886,7 +934,7 @@ st_widget_real_style_changed (StWidget *self)
       has_changed = TRUE;
       relayout_needed = TRUE;
     }
-  else if (border_width > 0 && border_color.alpha != 0)
+  else if (priv->border_width > 0 && priv->border_color.alpha != 0)
     {
       priv->draw_bg_color = TRUE;
       priv->draw_border_internal = TRUE;
@@ -917,15 +965,6 @@ st_widget_real_style_changed (StWidget *self)
       has_changed = TRUE;
       relayout_needed = TRUE;
     }
-  else if (priv->bg_gradient_type != ST_GRADIENT_NONE)
-    {
-      texture = g_object_new (CLUTTER_TYPE_CAIRO_TEXTURE, NULL);
-      priv->background_image = CLUTTER_ACTOR (texture);
-      clutter_actor_set_parent (priv->background_image,
-                                CLUTTER_ACTOR (self));
-      has_changed = TRUE;
-      relayout_needed = TRUE;
-    }
 
   /* CSS based drop shadows
    *



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