[gtk+/client-side-decorations: 80/92] Drop shadows



commit d2a2bc374f309a1c6b094d15aa58a5cdef22bd44
Author: Cody Russell <bratsche gnome org>
Date:   Mon Nov 30 09:36:27 2009 -0600

    Drop shadows

 gtk/gtkstyle.c  |  267 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 gtk/gtkwindow.c |  188 +++++++++++++++++++++++++++++----------
 2 files changed, 402 insertions(+), 53 deletions(-)
---
diff --git a/gtk/gtkstyle.c b/gtk/gtkstyle.c
index 8c9917e..f8c164c 100644
--- a/gtk/gtkstyle.c
+++ b/gtk/gtkstyle.c
@@ -26,6 +26,7 @@
 
 #include "config.h"
 #include <math.h>
+#include <pixman.h>
 #include <stdlib.h>
 #include <string.h>
 #include <gobject/gvaluecollector.h>
@@ -3491,6 +3492,244 @@ option_menu_get_props (GtkWidget      *widget,
     *indicator_spacing = default_option_indicator_spacing;
 }
 
+/* gaussian blur kernel */
+pixman_fixed_t *
+create_blur_kernel (gint     radius,
+                    gint    *length)
+{
+  const gdouble   radiusf = fabs (radius) + 1.0f;
+  const gdouble   sigma = sqrt (-(radiusf * radiusf) / (2.0f * log (1.0f / 255.0f)));
+  const gdouble   scale2 = 2.0f * sigma * sigma;
+  const gdouble   scale1 = 1.0f / (G_PI * scale2);
+  const gint      size = 2 * radius + 1;
+  const gint      n_params = size * size;
+  pixman_fixed_t *params;
+  gdouble        *tmp;
+  gdouble         sum;
+  gint            x;
+  gint            y;
+  gint            i;
+
+  tmp = g_newa (double, n_params);
+
+  for (i = 0, sum = 0, x = -radius; x <= radius; ++x)
+    {
+      for (y = -radius; y <= radius; ++y, ++i)
+        {
+          const gdouble u = x * x;
+          const gdouble v = y * y;
+
+          tmp[i] = scale1 * exp (-(u + v) / scale2);
+
+          sum += tmp[i];
+        }
+    }
+
+  params = g_new (pixman_fixed_t, n_params + 2);
+
+  params[0] = pixman_int_to_fixed (size);
+  params[1] = pixman_int_to_fixed (size);
+
+  for (i = 0; i < n_params; ++i)
+    params[2 + i] = pixman_double_to_fixed (tmp[i] / sum);
+
+  if (length)
+    *length = n_params + 2;
+
+  return params;
+}
+
+static void
+blur_image_surface (cairo_surface_t *surface,
+                    gint             radius)
+{
+  cairo_format_t  format;
+  pixman_fixed_t *params = NULL;
+  gint            n_params;
+  pixman_image_t *src;
+  gint            w;
+  gint            h;
+  gint            s;
+  gpointer        p;
+
+  if (radius == 0 ||
+      cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS ||
+      cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_IMAGE)
+    {
+      return;
+    }
+
+  format = cairo_image_surface_get_format (surface);
+  if (format != CAIRO_FORMAT_A8    &&
+      format != CAIRO_FORMAT_RGB24 &&
+      format != CAIRO_FORMAT_ARGB32)
+    {
+      return;
+    }
+
+  cairo_surface_flush (surface);
+
+  w = cairo_image_surface_get_width (surface);
+  h = cairo_image_surface_get_height (surface);
+  s = cairo_image_surface_get_stride (surface);
+
+  p = cairo_image_surface_get_data (surface);
+  src = pixman_image_create_bits (PIXMAN_a8r8g8b8, w, h, p, s);
+
+  params = create_blur_kernel (radius, &n_params);
+  pixman_image_set_filter (src,
+                           PIXMAN_FILTER_CONVOLUTION,
+                           params,
+                           n_params);
+  g_free (params);
+
+  pixman_image_composite (PIXMAN_OP_SRC,
+                          src,
+                          NULL,
+                          src,
+                          0, 0,
+                          0, 0,
+                          0, 0,
+                          w, h);
+
+  pixman_image_unref (src);
+
+  cairo_surface_mark_dirty (surface);
+}
+
+typedef struct _TileData
+{
+  guchar *data;
+  cairo_format_t format;
+  gint width;
+  gint height;
+  gint stride;
+} TileData;
+
+/* XXX - leak */
+static TileData *tile = NULL;
+
+static void
+paint_window_shadow (cairo_t *cr,
+                     gint     width,
+                     gint     height,
+                     gint     shadow_radius,
+                     gint     corner_radius)
+{
+  cairo_surface_t *tmp_surface = NULL;
+  cairo_surface_t *new_surface = NULL;
+  cairo_pattern_t *pattern     = NULL;
+  cairo_t         *cr_surf     = NULL;
+  cairo_matrix_t   matrix;
+
+  if (!tile)
+    {
+      guchar *data;
+
+      tmp_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                                4 * shadow_radius,
+                                                4 * shadow_radius);
+      if (cairo_surface_status (tmp_surface) != CAIRO_STATUS_SUCCESS)
+        return;
+
+      cr_surf = cairo_create (tmp_surface);
+      if (cairo_status (cr_surf) != CAIRO_STATUS_SUCCESS)
+        {
+          cairo_surface_destroy (tmp_surface);
+          return;
+        }
+
+      cairo_scale (cr_surf, 1.0f, 1.0f);
+      cairo_set_operator (cr_surf, CAIRO_OPERATOR_CLEAR);
+      cairo_paint (cr_surf);
+      cairo_set_operator (cr_surf, CAIRO_OPERATOR_OVER);
+      cairo_set_source_rgba (cr_surf, 0.0f, 0.0f, 0.0f, 0.75f);
+      cairo_arc (cr_surf,
+                 2 * shadow_radius,
+                 2 * shadow_radius,
+                 2.0f * corner_radius,
+                 0.0f,
+                 360.0f * (G_PI / 180.f));
+      cairo_fill (cr_surf);
+      cairo_destroy (cr_surf);
+
+      blur_image_surface (tmp_surface, shadow_radius);
+
+      tile = g_new0 (TileData, 1);
+      tile->format = cairo_image_surface_get_format (tmp_surface);
+      tile->width = cairo_image_surface_get_width (tmp_surface) / 2;
+      tile->height = cairo_image_surface_get_height (tmp_surface) / 2;
+      tile->stride = cairo_image_surface_get_stride (tmp_surface);
+
+      data = cairo_image_surface_get_data (tmp_surface);
+
+      tile->data = g_malloc (tile->height * tile->stride);
+      memcpy (tile->data, data, tile->height * tile->stride);
+    }
+
+  new_surface = cairo_image_surface_create_for_data (tile->data,
+                                                     tile->format,
+                                                     tile->width,
+                                                     tile->height,
+                                                     tile->stride);
+
+  pattern = cairo_pattern_create_for_surface (new_surface);
+  if (cairo_pattern_status (pattern) != CAIRO_STATUS_SUCCESS)
+    {
+      cairo_surface_destroy (tmp_surface);
+      cairo_surface_destroy (new_surface);
+      return;
+    }
+
+  /* top left */
+  cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
+  cairo_set_source (cr, pattern);
+  cairo_rectangle (cr,
+                   0.0f,
+                   0.0f,
+                   width - 2 * shadow_radius,
+                   2 * shadow_radius);
+  cairo_fill (cr);
+
+  /* bottom left */
+  cairo_matrix_init_scale (&matrix, 1.0f, -1.0f);
+  cairo_matrix_translate (&matrix, 0.0f, -height);
+  cairo_pattern_set_matrix (pattern, &matrix);
+  cairo_rectangle (cr,
+                   0.0f,
+                   2 * shadow_radius,
+                   2 * shadow_radius,
+                   height - 2 * shadow_radius);
+  cairo_fill (cr);
+
+  /* top right */
+  cairo_matrix_init_scale (&matrix, -1.0f, 1.0f);
+  cairo_matrix_translate (&matrix, -width, 0.0f);
+  cairo_pattern_set_matrix (pattern, &matrix);
+  cairo_rectangle (cr,
+                   width - 2 * shadow_radius,
+                   0.0f,
+                   2 * shadow_radius,
+                   height - 2 * shadow_radius);
+  cairo_fill (cr);
+
+  /* bottom right */
+  cairo_matrix_init_scale (&matrix, -1.0f, -1.0f);
+  cairo_matrix_translate (&matrix, -width, -height);
+  cairo_pattern_set_matrix (pattern, &matrix);
+  cairo_rectangle (cr,
+                   2 * shadow_radius,
+                   height - 2 * shadow_radius,
+                   width - 2 * shadow_radius,
+                   2 * shadow_radius);
+  cairo_fill (cr);
+
+
+  cairo_pattern_destroy (pattern);
+  cairo_surface_destroy (tmp_surface);
+  cairo_surface_destroy (new_surface);
+}
+
 static void
 paint_decorated_window (GtkStyle *style,
                         GdkWindow *window,
@@ -3506,14 +3745,16 @@ paint_decorated_window (GtkStyle *style,
 {
   cairo_pattern_t *gradient;
   cairo_t *cr;
-  const double hmargin = 2.5, vmargin = 2.5, radius = 5;
+  const double hmargin = 2.5 + x;
+  const double vmargin = 2.5 + y;
+  const double radius = 5;
   GdkColor *color;
   GdkWindowState state;
 
   if (width == -1)
-    width = widget->allocation.width;
+    width = widget->allocation.width - 2 * x;
   if (height == -1)
-    height = widget->allocation.height;
+    height = widget->allocation.height - 2 * y;
 
   state = gdk_window_get_state (window);
 
@@ -3531,6 +3772,14 @@ paint_decorated_window (GtkStyle *style,
     }
   else
     {
+      paint_window_shadow (cr,
+                           width,
+                           height,
+                           x / 2 + 2.5,
+                           radius);
+
+      cairo_move_to (cr, hmargin, vmargin + radius);
+
       cairo_arc (cr, hmargin + radius, vmargin + radius,
                  radius, M_PI, 3 * M_PI / 2);
       cairo_line_to (cr, width - hmargin - radius, vmargin);
@@ -3673,11 +3922,15 @@ gtk_default_draw_box (GtkStyle      *style,
     }
 
   if (strcmp (detail, "decoration") == 0)
-    paint_decorated_window (style, window, state_type, shadow_type,
-			    area, widget, detail, x, y, width, height);
+    {
+      paint_decorated_window (style, window, state_type, shadow_type,
+                              area, widget, detail, x, y, width, height);
+    }
   else
-    gtk_paint_shadow (style, window, state_type, shadow_type,
-		      area, widget, detail, x, y, width, height);
+    {
+      gtk_paint_shadow (style, window, state_type, shadow_type,
+                        area, widget, detail, x, y, width, height);
+    }
 
   if (detail && strcmp (detail, "optionmenu") == 0)
     {
diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
index 0761b12..791da9e 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -898,13 +898,15 @@ gtk_window_class_init (GtkWindowClass *klass)
                                            g_param_spec_int ("decoration-border-width",
                                                              P_("Decoration border width"),
                                                              P_("Decoration border width"),
-                                                             0, G_MAXINT, 6, GTK_PARAM_READWRITE));
+                                                             0, G_MAXINT,
+                                                             6, GTK_PARAM_READWRITE));
 
   gtk_widget_class_install_style_property (widget_class,
                                            g_param_spec_int ("decoration-resize-handle",
                                                              P_("Decoration resize handle size"),
                                                              P_("Decoration resize handle size"),
-                                                             0, G_MAXINT, 20, GTK_PARAM_READWRITE));
+                                                             0, G_MAXINT,
+                                                             20, GTK_PARAM_READWRITE));
 
   /**
    * GtkWindow:decoration-extents:
@@ -913,10 +915,37 @@ gtk_window_class_init (GtkWindowClass *klass)
    * used for client-side window drop-shadows or window glow.
    */
   gtk_widget_class_install_style_property (widget_class,
-                                           g_param_spec_int ("decoration-extents",
-                                                             P_("Decoration extent size"),
-                                                             P_("Decoration extent size"),
-                                                             0, G_MAXINT, 0, GTK_PARAM_READWRITE));
+                                           g_param_spec_int ("extents-left",
+                                                             P_("Left extents"),
+                                                             P_("Left extents area"),
+                                                             0, G_MAXINT,
+                                                             40,
+                                                             GTK_PARAM_READWRITE));
+
+  gtk_widget_class_install_style_property (widget_class,
+                                           g_param_spec_int ("extents-top",
+                                                             P_("Top extents"),
+                                                             P_("Top extents area"),
+                                                             0, G_MAXINT,
+                                                             40,
+                                                             GTK_PARAM_READWRITE));
+
+  gtk_widget_class_install_style_property (widget_class,
+                                           g_param_spec_int ("extents-right",
+                                                             P_("Right extents"),
+                                                             P_("Right extents area"),
+                                                             0, G_MAXINT,
+                                                             40,
+                                                             GTK_PARAM_READWRITE));
+
+  gtk_widget_class_install_style_property (widget_class,
+                                           g_param_spec_int ("extents-bottom",
+                                                             P_("Bottom extents"),
+                                                             P_("Bottom extents area"),
+                                                             0, G_MAXINT,
+                                                             40,
+                                                             GTK_PARAM_READWRITE));
+
 
   window_signals[SET_FOCUS] =
     g_signal_new (I_("set-focus"),
@@ -5509,29 +5538,39 @@ gtk_window_size_request (GtkWidget      *widget,
   GtkBin *bin;
   GtkRequisition child_requisition;
   GtkWindowPrivate *priv;
-  gint frame_width = 0;
-  gint extents = 0;
 
   window = GTK_WINDOW (widget);
   priv = GTK_WINDOW_GET_PRIVATE (window);
   bin = GTK_BIN (window);
 
-  if (priv->client_side_decorations & GDK_DECOR_BORDER)
-    {
-      frame_width = get_decoration_frame_width (window);
-
-      gtk_widget_style_get (widget,
-                            "decoration-extents", &extents,
-                            NULL);
-    }
-
   requisition->width = GTK_CONTAINER (window)->border_width * 2;
   requisition->height = GTK_CONTAINER (window)->border_width * 2;
 
   if (is_client_side_decorated (window))
     {
-      requisition->width += extents * 2;
-      requisition->height += extents * 2;
+      gint frame_width = 0;
+      gint extents_left = 0, extents_right = 0, extents_top = 0, extents_bottom = 0;
+      GdkWindowState state;
+
+      if (widget->window != NULL)
+        state = gdk_window_get_state (widget->window);
+
+      if (widget->window && !(state & GDK_WINDOW_STATE_MAXIMIZED))
+        {
+          gtk_widget_style_get (widget,
+                                "extents-left",   &extents_left,
+                                "extents-top",    &extents_top,
+                                "extents-right",  &extents_right,
+                                "extents-bottom", &extents_bottom,
+                                NULL);
+        }
+
+      if (priv->client_side_decorations & GDK_DECOR_BORDER)
+        {
+          gtk_widget_style_get (widget,
+                                "decoration-border-width", &frame_width,
+                                NULL);
+        }
 
       if (window->type != GTK_WINDOW_POPUP)
         {
@@ -5563,6 +5602,9 @@ gtk_window_size_request (GtkWidget      *widget,
           requisition->width += frame_width * 2;
           requisition->height += frame_width * 2 + child_height;
         }
+
+      requisition->width += extents_left + extents_right;
+      requisition->height += extents_top + extents_bottom;
     }
 
   if (bin->child && gtk_widget_get_visible (bin->child))
@@ -5623,6 +5665,11 @@ gtk_window_size_allocate (GtkWidget     *widget,
   gint left_width = 0;
   GdkRectangle rect;
   gboolean client_decorated;
+  gint extents_left = 0;
+  gint extents_top = 0;
+  gint extents_right = 0;
+  gint extents_bottom = 0;
+  GdkWindowState state;
 
   window = GTK_WINDOW (widget);
   container = GTK_CONTAINER (widget);
@@ -5630,20 +5677,38 @@ gtk_window_size_allocate (GtkWidget     *widget,
   priv = GTK_WINDOW_GET_PRIVATE (window);
 
   client_decorated = is_client_side_decorated (window);
+  if (widget->window)
+    state = gdk_window_get_state (widget->window);
 
   deco_allocation.width = deco_allocation.height = 0;
 
-  frame_width = get_decoration_frame_width (window);
+  if (client_decorated)
+    {
+      if (widget->window && !(state & GDK_WINDOW_STATE_MAXIMIZED))
+        {
+          gtk_widget_style_get (widget,
+                                "extents-left",   &extents_left,
+                                "extents-top",    &extents_top,
+                                "extents-right",  &extents_right,
+                                "extents-bottom", &extents_bottom,
+                                NULL);
+        }
+
+      if (priv->client_side_decorations & GDK_DECOR_BORDER)
+        {
+          frame_width = get_decoration_frame_width (window);
+        }
+    }
 
   if (client_decorated && priv->icon_event_box && gtk_widget_get_visible (priv->icon_event_box))
     {
       gtk_widget_get_child_requisition (priv->icon_event_box, &deco_requisition);
 
       if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
-        deco_allocation.x = allocation->width - frame_width - deco_requisition.width;
+        deco_allocation.x = allocation->width - frame_width - deco_requisition.width - extents_left;
       else
-        deco_allocation.x = frame_width;
-      deco_allocation.y = frame_width;
+        deco_allocation.x = frame_width + extents_left;
+      deco_allocation.y = frame_width + extents_top;
       deco_allocation.width = deco_requisition.width;
       deco_allocation.height = deco_requisition.height;
 
@@ -5658,10 +5723,10 @@ gtk_window_size_allocate (GtkWidget     *widget,
       gtk_widget_get_child_requisition (priv->button_box, &box_requisition);
 
       if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
-        box_allocation.x = frame_width;
+        box_allocation.x = frame_width + extents_left;
       else
-        box_allocation.x = allocation->width - frame_width - box_requisition.width;
-      box_allocation.y = frame_width;
+        box_allocation.x = allocation->width - frame_width - box_requisition.width - extents_right;
+      box_allocation.y = frame_width + extents_top;
       box_allocation.width = box_requisition.width;
       box_allocation.height = box_requisition.height;
 
@@ -5675,8 +5740,8 @@ gtk_window_size_allocate (GtkWidget     *widget,
     {
       gtk_widget_get_child_requisition (priv->title_label, &deco_requisition);
 
-      deco_allocation.x = 2 * frame_width + left_width;
-      deco_allocation.y = frame_width;
+      deco_allocation.x = 2 * frame_width + left_width + extents_left;
+      deco_allocation.y = frame_width + extents_top;
       deco_allocation.width = MAX (deco_requisition.width, get_available_size_for_label (window));
       deco_allocation.height = deco_requisition.height;
 
@@ -5701,31 +5766,33 @@ gtk_window_size_allocate (GtkWidget     *widget,
     {
       if (client_decorated && window->type != GTK_WINDOW_POPUP)
         {
-          child_allocation.x = container->border_width + frame_width;
-          child_allocation.y = container->border_width
+          child_allocation.x = container->border_width + frame_width + extents_left;
+          child_allocation.y = container->border_width + extents_top
             + MAX (deco_allocation.height, box_allocation.height)
             + frame_width; // XXX - padding style property?
           child_allocation.width = MAX (1, ((gint)allocation->width - container->border_width * 2
+                                            - extents_left - extents_right
                                             - (frame_width * 2)));
           child_allocation.height = MAX (1, ((gint)allocation->height - container->border_width * 2
+                                             - extents_top - extents_bottom
                                              - (frame_width * 2)
                                              // XXX - padding style property?
                                              - MAX (deco_allocation.height, box_allocation.height)));
         }
       else
         {
-          child_allocation.x = GTK_CONTAINER (window)->border_width;
-          child_allocation.y = GTK_CONTAINER (window)->border_width;
-          child_allocation.width =
-            MAX (1, (gint)allocation->width - child_allocation.x * 2);
-          child_allocation.height =
-            MAX (1, (gint)allocation->height - child_allocation.y * 2);
+          child_allocation.x = GTK_CONTAINER (window)->border_width + extents_left;
+          child_allocation.y = GTK_CONTAINER (window)->border_width + extents_top;
+          child_allocation.width = MAX (1,
+              (gint)allocation->width - child_allocation.x * 2 - extents_left - extents_right);
+          child_allocation.height = MAX (1,
+              (gint)allocation->height - child_allocation.y * 2 - extents_top - extents_bottom);
         }
 
       gtk_widget_size_allocate (window->bin.child, &child_allocation);
     }
 
-  if  (widget->window != NULL)
+  if (widget->window != NULL)
     {
       rect.x = 0;
       rect.y = 0;
@@ -7881,17 +7948,46 @@ gtk_window_paint (GtkWidget     *widget,
   if (is_client_side_decorated (GTK_WINDOW (widget)))
     {
       gint frame_width, title_height;
+      gint extents_left, extents_top, extents_right, extents_bottom;
+
+      frame_width = title_height = extents_left = extents_right = extents_top = extents_bottom = 0;
 
-      frame_width = get_decoration_frame_width (GTK_WINDOW (widget));
-      title_height = get_title_height (GTK_WINDOW (widget));
+      if (is_client_side_decorated (GTK_WINDOW (widget)))
+        {
+          frame_width = get_decoration_frame_width (GTK_WINDOW (widget));
+          title_height = get_title_height (GTK_WINDOW (widget));
+
+          gtk_widget_style_get (widget,
+                                "decoration-border-width", &frame_width,
+                                "extents-left",            &extents_left,
+                                "extents-top",             &extents_top,
+                                "extents-right",           &extents_right,
+                                "extents-bottom",          &extents_bottom,
+                                NULL);
+        }
 
-      gtk_paint_box (widget->style, widget->window, GTK_STATE_NORMAL,
-		     GTK_SHADOW_OUT, area, widget, "decoration", 0, 0, -1, -1);
-      gtk_paint_flat_box (widget->style, widget->window, GTK_STATE_NORMAL,
-                          GTK_SHADOW_NONE, area, widget, "base",
-                          frame_width, frame_width + title_height,
-                          widget->allocation.width - 2 * frame_width,
-                          widget->allocation.height - 2 * frame_width - title_height);
+      gtk_paint_box (widget->style,
+                     widget->window,
+                     GTK_STATE_NORMAL,
+		     GTK_SHADOW_OUT,
+                     area, widget,
+                     "decoration",
+                     extents_left,
+                     extents_top,
+                     -1,
+                     -1);
+
+      gtk_paint_flat_box (widget->style,
+                          widget->window,
+                          GTK_STATE_NORMAL,
+                          GTK_SHADOW_NONE,
+                          area,
+                          widget,
+                          "base",
+                          frame_width + extents_left,
+                          frame_width + title_height + extents_top,
+                          widget->allocation.width - 2 * frame_width - extents_left - extents_right,
+                          widget->allocation.height - 2 * frame_width - title_height - extents_top - extents_bottom);
     }
   else
     gtk_paint_flat_box (widget->style, widget->window, GTK_STATE_NORMAL,



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