]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkcssshadowvalue.c
GtkBubbleWindow: rework how drawing is done
[~andy/gtk] / gtk / gtkcssshadowvalue.c
index a74a992ccbfede793c86234ec2f7bc2725eef056..618288807af8d3b7e9571d013c632880a9f7f67a 100644 (file)
 
 #include "gtkcssshadowvalueprivate.h"
 
+#include "gtkcairoblurprivate.h"
+#include "gtkcsscolorvalueprivate.h"
+#include "gtkcssnumbervalueprivate.h"
+#include "gtkcssrgbavalueprivate.h"
 #include "gtkstylecontextprivate.h"
 #include "gtkthemingengineprivate.h"
 #include "gtkpango.h"
@@ -29,83 +33,139 @@ struct _GtkCssValue {
   GTK_CSS_VALUE_BASE
   guint inset :1;
 
-  gint16 hoffset;
-  gint16 voffset;
-  gint16 radius;
-  gint16 spread;
+  GtkCssValue *hoffset;
+  GtkCssValue *voffset;
+  GtkCssValue *radius;
+  GtkCssValue *spread;
 
-  GdkRGBA color;
-  GtkSymbolicColor *symbolic_color;
+  GtkCssValue *color;
 };
 
+static GtkCssValue *    gtk_css_shadow_value_new (GtkCssValue *hoffset,
+                                                  GtkCssValue *voffset,
+                                                  GtkCssValue *radius,
+                                                  GtkCssValue *spread,
+                                                  gboolean     inset,
+                                                  GtkCssValue *color);
+
 static void
 gtk_css_value_shadow_free (GtkCssValue *shadow)
 {
-  if (shadow->symbolic_color != NULL)
-    gtk_symbolic_color_unref (shadow->symbolic_color);
+  _gtk_css_value_unref (shadow->hoffset);
+  _gtk_css_value_unref (shadow->voffset);
+  _gtk_css_value_unref (shadow->radius);
+  _gtk_css_value_unref (shadow->spread);
+  _gtk_css_value_unref (shadow->color);
 
   g_slice_free (GtkCssValue, shadow);
 }
 
+static GtkCssValue *
+gtk_css_value_shadow_compute (GtkCssValue             *shadow,
+                              guint                    property_id,
+                              GtkStyleProviderPrivate *provider,
+                              GtkCssComputedValues    *values,
+                              GtkCssComputedValues    *parent_values,
+                              GtkCssDependencies      *dependencies)
+{
+  GtkCssValue *hoffset, *voffset, *radius, *spread, *color;
+  GtkCssDependencies child_deps;
+
+  child_deps = 0;
+  hoffset = _gtk_css_value_compute (shadow->hoffset, property_id, provider, values, parent_values, &child_deps);
+  *dependencies = _gtk_css_dependencies_union (*dependencies, child_deps);
+
+  child_deps = 0;
+  voffset = _gtk_css_value_compute (shadow->voffset, property_id, provider, values, parent_values, &child_deps);
+  *dependencies = _gtk_css_dependencies_union (*dependencies, child_deps);
+
+  child_deps = 0;
+  radius = _gtk_css_value_compute (shadow->radius, property_id, provider, values, parent_values, &child_deps);
+  *dependencies = _gtk_css_dependencies_union (*dependencies, child_deps);
+
+  child_deps = 0;
+  spread = _gtk_css_value_compute (shadow->spread, property_id, provider, values, parent_values, &child_deps),
+  *dependencies = _gtk_css_dependencies_union (*dependencies, child_deps);
+
+  child_deps = 0;
+  color = _gtk_css_value_compute (shadow->color, property_id, provider, values, parent_values, &child_deps);
+  *dependencies = _gtk_css_dependencies_union (*dependencies, child_deps);
+
+  return gtk_css_shadow_value_new (hoffset, voffset, radius, spread, shadow->inset, color);
+}
+
 static gboolean
 gtk_css_value_shadow_equal (const GtkCssValue *shadow1,
                             const GtkCssValue *shadow2)
 {
-  /* FIXME */
-  return shadow1 == shadow2;
+  return shadow1->inset == shadow2->inset
+      && _gtk_css_value_equal (shadow1->hoffset, shadow2->hoffset)
+      && _gtk_css_value_equal (shadow1->voffset, shadow2->voffset)
+      && _gtk_css_value_equal (shadow1->radius, shadow2->radius)
+      && _gtk_css_value_equal (shadow1->spread, shadow2->spread)
+      && _gtk_css_value_equal (shadow1->color, shadow2->color);
 }
 
 static GtkCssValue *
 gtk_css_value_shadow_transition (GtkCssValue *start,
                                  GtkCssValue *end,
+                                 guint        property_id,
                                  double       progress)
 {
-  return NULL;
+  if (start->inset != end->inset)
+    return NULL;
+
+  return gtk_css_shadow_value_new (_gtk_css_value_transition (start->hoffset, end->hoffset, property_id, progress),
+                                   _gtk_css_value_transition (start->voffset, end->voffset, property_id, progress),
+                                   _gtk_css_value_transition (start->radius, end->radius, property_id, progress),
+                                   _gtk_css_value_transition (start->spread, end->spread, property_id, progress),
+                                   start->inset,
+                                   _gtk_css_value_transition (start->color, end->color, property_id, progress));
 }
 
 static void
 gtk_css_value_shadow_print (const GtkCssValue *shadow,
                             GString           *string)
 {
-  gchar *color_str;
-
-  if (shadow->inset)
-    g_string_append (string, "inset ");
-
-  g_string_append_printf (string, "%d %d ",
-                          (gint) shadow->hoffset,
-                          (gint) shadow->voffset);
+  _gtk_css_value_print (shadow->hoffset, string);
+  g_string_append_c (string, ' ');
+  _gtk_css_value_print (shadow->voffset, string);
+  g_string_append_c (string, ' ');
+  if (_gtk_css_number_value_get (shadow->radius, 100) != 0 ||
+      _gtk_css_number_value_get (shadow->spread, 100) != 0)
+    {
+      _gtk_css_value_print (shadow->radius, string);
+      g_string_append_c (string, ' ');
+    }
 
-  if (shadow->radius != 0)
-    g_string_append_printf (string, "%d ", (gint) shadow->radius);
+  if (_gtk_css_number_value_get (shadow->spread, 100) != 0)
+    {
+      _gtk_css_value_print (shadow->spread, string);
+      g_string_append_c (string, ' ');
+    }
 
-  if (shadow->spread != 0)
-    g_string_append_printf (string, "%d ", (gint) shadow->spread);
+  _gtk_css_value_print (shadow->color, string);
 
-  if (shadow->symbolic_color != NULL)
-    color_str = gtk_symbolic_color_to_string (shadow->symbolic_color);
-  else
-    color_str = gdk_rgba_to_string (&shadow->color);
+  if (shadow->inset)
+    g_string_append (string, " inset");
 
-  g_string_append (string, color_str);
-  g_free (color_str);
 }
 
 static const GtkCssValueClass GTK_CSS_VALUE_SHADOW = {
   gtk_css_value_shadow_free,
+  gtk_css_value_shadow_compute,
   gtk_css_value_shadow_equal,
   gtk_css_value_shadow_transition,
   gtk_css_value_shadow_print
 };
 
 static GtkCssValue *
-gtk_css_shadow_value_new (gdouble hoffset,
-                          gdouble voffset,
-                          gdouble radius,
-                          gdouble spread,
-                          gboolean inset,
-                          GdkRGBA *color,
-                          GtkSymbolicColor *symbolic_color)
+gtk_css_shadow_value_new (GtkCssValue *hoffset,
+                          GtkCssValue *voffset,
+                          GtkCssValue *radius,
+                          GtkCssValue *spread,
+                          gboolean     inset,
+                          GtkCssValue *color)
 {
   GtkCssValue *retval;
 
@@ -116,95 +176,194 @@ gtk_css_shadow_value_new (gdouble hoffset,
   retval->radius = radius;
   retval->spread = spread;
   retval->inset = inset;
-
-  retval->symbolic_color = symbolic_color;
-
-  if (color != NULL)
-    retval->color = *color;
+  retval->color = color;
 
   return retval;
 }                  
 
+GtkCssValue *
+_gtk_css_shadow_value_new_for_transition (GtkCssValue *target)
+{
+  GdkRGBA transparent = { 0, 0, 0, 0 };
+
+  g_return_val_if_fail (target->class == &GTK_CSS_VALUE_SHADOW, NULL);
+
+  return gtk_css_shadow_value_new (_gtk_css_number_value_new (0, GTK_CSS_PX),
+                                   _gtk_css_number_value_new (0, GTK_CSS_PX),
+                                   _gtk_css_number_value_new (0, GTK_CSS_PX),
+                                   _gtk_css_number_value_new (0, GTK_CSS_PX),
+                                   target->inset,
+                                   _gtk_css_rgba_value_new_from_rgba (&transparent));
+}
+
+static gboolean
+value_is_done_parsing (GtkCssParser *parser)
+{
+  return _gtk_css_parser_is_eof (parser) ||
+         _gtk_css_parser_begins_with (parser, ',') ||
+         _gtk_css_parser_begins_with (parser, ';') ||
+         _gtk_css_parser_begins_with (parser, '}');
+}
+
 GtkCssValue *
 _gtk_css_shadow_value_parse (GtkCssParser *parser)
 {
-  gboolean have_inset, have_color, have_lengths;
-  gdouble hoffset, voffset, blur, spread;
-  GtkSymbolicColor *color;
+  enum {
+    HOFFSET,
+    VOFFSET,
+    RADIUS,
+    SPREAD,
+    COLOR,
+    N_VALUES
+  };
+  GtkCssValue *values[N_VALUES] = { NULL, };
+  gboolean inset;
   guint i;
 
-  have_inset = have_lengths = have_color = FALSE;
-
-  for (i = 0; i < 3; i++)
+  inset = _gtk_css_parser_try (parser, "inset", TRUE);
+
+  do
+  {
+    if (values[HOFFSET] == NULL &&
+         _gtk_css_parser_has_number (parser))
+      {
+        values[HOFFSET] = _gtk_css_number_value_parse (parser,
+                                                       GTK_CSS_PARSE_LENGTH
+                                                       | GTK_CSS_NUMBER_AS_PIXELS);
+        if (values[HOFFSET] == NULL)
+          goto fail;
+
+        values[VOFFSET] = _gtk_css_number_value_parse (parser,
+                                                       GTK_CSS_PARSE_LENGTH
+                                                       | GTK_CSS_NUMBER_AS_PIXELS);
+        if (values[VOFFSET] == NULL)
+          goto fail;
+
+        if (_gtk_css_parser_has_number (parser))
+          {
+            values[RADIUS] = _gtk_css_number_value_parse (parser,
+                                                          GTK_CSS_PARSE_LENGTH
+                                                          | GTK_CSS_POSITIVE_ONLY
+                                                          | GTK_CSS_NUMBER_AS_PIXELS);
+            if (values[RADIUS] == NULL)
+              goto fail;
+          }
+        else
+          values[RADIUS] = _gtk_css_number_value_new (0.0, GTK_CSS_PX);
+                                                        
+        if (_gtk_css_parser_has_number (parser))
+          {
+            values[SPREAD] = _gtk_css_number_value_parse (parser,
+                                                          GTK_CSS_PARSE_LENGTH
+                                                          | GTK_CSS_NUMBER_AS_PIXELS);
+            if (values[SPREAD] == NULL)
+              goto fail;
+          }
+        else
+          values[SPREAD] = _gtk_css_number_value_new (0.0, GTK_CSS_PX);
+      }
+    else if (!inset && _gtk_css_parser_try (parser, "inset", TRUE))
+      {
+        if (values[HOFFSET] == NULL)
+          goto fail;
+        inset = TRUE;
+        break;
+      }
+    else if (values[COLOR] == NULL)
+      {
+        values[COLOR] = _gtk_css_color_value_parse (parser);
+
+        if (values[COLOR] == NULL)
+          goto fail;
+      }
+    else
+      {
+        /* We parsed everything and there's still stuff left?
+         * Pretend we didn't notice and let the normal code produce
+         * a 'junk at end of value' error */
+        goto fail;
+      }
+  }
+  while (values[HOFFSET] == NULL || !value_is_done_parsing (parser));
+
+  if (values[COLOR] == NULL)
+    values[COLOR] = _gtk_css_color_value_new_current_color ();
+
+  return gtk_css_shadow_value_new (values[HOFFSET], values[VOFFSET],
+                                   values[RADIUS], values[SPREAD],
+                                   inset, values[COLOR]);
+
+fail:
+  for (i = 0; i < N_VALUES; i++)
     {
-      if (!have_inset && 
-          _gtk_css_parser_try (parser, "inset", TRUE))
-        {
-          have_inset = TRUE;
-          continue;
-        }
-        
-      if (!have_lengths &&
-          _gtk_css_parser_try_double (parser, &hoffset))
-        {
-          have_lengths = TRUE;
-
-          if (!_gtk_css_parser_try_double (parser, &voffset))
-            {
-              _gtk_css_parser_error (parser, "Horizontal and vertical offsets are required");
-              if (have_color)
-                gtk_symbolic_color_unref (color);
-              return NULL;
-            }
-
-          if (!_gtk_css_parser_try_double (parser, &blur))
-            blur = 0;
-
-          if (!_gtk_css_parser_try_double (parser, &spread))
-            spread = 0;
-
-          continue;
-        }
-
-      if (!have_color)
-        {
-          have_color = TRUE;
-
-          /* XXX: the color is optional and UA-defined if it's missing,
-           * but it doesn't really make sense for us...
-           */
-          color = _gtk_css_parser_read_symbolic_color (parser);
-
-          if (color == NULL)
-            return NULL;
-        }
+      if (values[i])
+        _gtk_css_value_unref (values[i]);
     }
 
-  if (!have_color || !have_lengths)
+  return NULL;
+}
+
+static const cairo_user_data_key_t shadow_key;
+
+static cairo_t *
+gtk_css_shadow_value_start_drawing (const GtkCssValue *shadow,
+                                    cairo_t           *cr)
+{
+  cairo_rectangle_int_t clip_rect;
+  cairo_surface_t *surface;
+  cairo_t *blur_cr;
+  gdouble radius;
+
+  radius = _gtk_css_number_value_get (shadow->radius, 0);
+  if (radius == 0.0)
+    return cr;
+
+  gdk_cairo_get_clip_rectangle (cr, &clip_rect);
+
+  /* Create a larger surface to center the blur. */
+  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                        clip_rect.width + 2 * radius,
+                                        clip_rect.height + 2 * radius);
+  cairo_surface_set_device_offset (surface, radius - clip_rect.x, radius - clip_rect.y);
+  blur_cr = cairo_create (surface);
+  cairo_set_user_data (blur_cr, &shadow_key, cairo_reference (cr), (cairo_destroy_func_t) cairo_destroy);
+
+  if (cairo_has_current_point (cr))
     {
-      _gtk_css_parser_error (parser, "Must specify at least color and offsets");
-      return NULL;
+      double x, y;
+      
+      cairo_get_current_point (cr, &x, &y);
+      cairo_move_to (blur_cr, x, y);
     }
 
-  return gtk_css_shadow_value_new (hoffset, voffset,
-                                   blur, spread, have_inset,
-                                   NULL, color);
+  return blur_cr;
 }
 
-GtkCssValue *
-_gtk_css_shadow_value_compute (GtkCssValue     *shadow,
-                               GtkStyleContext *context)
+static cairo_t *
+gtk_css_shadow_value_finish_drawing (const GtkCssValue *shadow,
+                                     cairo_t           *cr)
 {
-  GdkRGBA color;
+  gdouble radius;
+  cairo_t *original_cr;
+  cairo_surface_t *surface;
+
+  radius = _gtk_css_number_value_get (shadow->radius, 0);
+  if (radius == 0.0)
+    return cr;
+
+  surface = cairo_get_target (cr);
+  original_cr = cairo_get_user_data (cr, &shadow_key);
+
+  /* Blur the surface. */
+  _gtk_cairo_blur_surface (surface, radius);
 
-  if (!_gtk_style_context_resolve_color (context,
-                                         shadow->symbolic_color,
-                                         &color))
-    color.red = color.blue = color.green = color.alpha = 0;
+  cairo_set_source_surface (original_cr, surface, 0, 0);
+  cairo_paint (original_cr);
 
-  return gtk_css_shadow_value_new (shadow->hoffset, shadow->voffset,
-                                   shadow->radius, shadow->spread, shadow->inset,
-                                   &color, NULL);
+  cairo_destroy (cr);
+  cairo_surface_destroy (surface);
+
+  return original_cr;
 }
 
 void
@@ -219,11 +378,20 @@ _gtk_css_shadow_value_paint_layout (const GtkCssValue *shadow,
 
   cairo_save (cr);
 
-  cairo_rel_move_to (cr, shadow->hoffset, shadow->voffset);
-  gdk_cairo_set_source_rgba (cr, &shadow->color);
+  cairo_rel_move_to (cr, 
+                     _gtk_css_number_value_get (shadow->hoffset, 0),
+                     _gtk_css_number_value_get (shadow->voffset, 0));
+
+  cr = gtk_css_shadow_value_start_drawing (shadow, cr);
+
+  gdk_cairo_set_source_rgba (cr, _gtk_css_rgba_value_get_rgba (shadow->color));
   _gtk_pango_fill_layout (cr, layout);
 
-  cairo_rel_move_to (cr, -shadow->hoffset, -shadow->voffset);
+  cr = gtk_css_shadow_value_finish_drawing (shadow, cr);
+
+  cairo_rel_move_to (cr,
+                     - _gtk_css_number_value_get (shadow->hoffset, 0),
+                     - _gtk_css_number_value_get (shadow->voffset, 0));
   cairo_restore (cr);
 }
 
@@ -237,11 +405,18 @@ _gtk_css_shadow_value_paint_icon (const GtkCssValue *shadow,
 
   cairo_save (cr);
   pattern = cairo_pattern_reference (cairo_get_source (cr));
-  gdk_cairo_set_source_rgba (cr, &shadow->color);
 
-  cairo_translate (cr, shadow->hoffset, shadow->voffset);
+  cr = gtk_css_shadow_value_start_drawing (shadow, cr);
+
+  gdk_cairo_set_source_rgba (cr, _gtk_css_rgba_value_get_rgba (shadow->color));
+
+  cairo_translate (cr,
+                   _gtk_css_number_value_get (shadow->hoffset, 0),
+                   _gtk_css_number_value_get (shadow->voffset, 0));
   cairo_mask (cr, pattern);
 
+  cr = gtk_css_shadow_value_finish_drawing (shadow, cr);
+
   cairo_restore (cr);
   cairo_pattern_destroy (pattern);
 }
@@ -256,10 +431,16 @@ _gtk_css_shadow_value_paint_spinner (const GtkCssValue *shadow,
 
   cairo_save (cr);
 
-  cairo_translate (cr, shadow->hoffset, shadow->voffset);
+  cr = gtk_css_shadow_value_start_drawing (shadow, cr);
+
+  cairo_translate (cr,
+                   _gtk_css_number_value_get (shadow->hoffset, 0),
+                   _gtk_css_number_value_get (shadow->voffset, 0));
   _gtk_theming_engine_paint_spinner (cr,
                                      radius, progress,
-                                     &shadow->color);
+                                     _gtk_css_rgba_value_get_rgba (shadow->color));
+
+  cr = gtk_css_shadow_value_finish_drawing (shadow, cr);
 
   cairo_restore (cr);
 }
@@ -269,27 +450,37 @@ _gtk_css_shadow_value_paint_box (const GtkCssValue   *shadow,
                                  cairo_t             *cr,
                                  const GtkRoundedBox *padding_box)
 {
-  GtkRoundedBox box;
+  GtkRoundedBox box, clip_box;
+  double spread, radius;
 
   g_return_if_fail (shadow->class == &GTK_CSS_VALUE_SHADOW);
 
   cairo_save (cr);
-  cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
 
   _gtk_rounded_box_path (padding_box, cr);
   cairo_clip (cr);
 
   box = *padding_box;
-  _gtk_rounded_box_move (&box, shadow->hoffset, shadow->voffset);
-  _gtk_rounded_box_shrink (&box,
-                           shadow->spread, shadow->spread,
-                           shadow->spread, shadow->spread);
+  _gtk_rounded_box_move (&box,
+                         _gtk_css_number_value_get (shadow->hoffset, 0),
+                         _gtk_css_number_value_get (shadow->voffset, 0));
+  spread = _gtk_css_number_value_get (shadow->spread, 0);
+  _gtk_rounded_box_shrink (&box, spread, spread, spread, spread);
+
+  clip_box = *padding_box;
+  radius = _gtk_css_number_value_get (shadow->radius, 0);
+  _gtk_rounded_box_shrink (&clip_box, -radius, -radius, -radius, -radius);
 
+  cr = gtk_css_shadow_value_start_drawing (shadow, cr);
+
+  cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
   _gtk_rounded_box_path (&box, cr);
-  _gtk_rounded_box_clip_path (padding_box, cr);
+  _gtk_rounded_box_clip_path (&clip_box, cr);
 
-  gdk_cairo_set_source_rgba (cr, &shadow->color);
+  gdk_cairo_set_source_rgba (cr, _gtk_css_rgba_value_get_rgba (shadow->color));
   cairo_fill (cr);
 
+  cr = gtk_css_shadow_value_finish_drawing (shadow, cr);
+
   cairo_restore (cr);
 }