#include "gtkcssshadowvalueprivate.h"
+#include "gtkcairoblurprivate.h"
+#include "gtkcsscolorvalueprivate.h"
+#include "gtkcssnumbervalueprivate.h"
+#include "gtkcssrgbavalueprivate.h"
#include "gtkstylecontextprivate.h"
#include "gtkthemingengineprivate.h"
#include "gtkpango.h"
-typedef struct _GtkShadowElement GtkShadowElement;
-
-struct _GtkShadowElement {
- gint16 hoffset;
- gint16 voffset;
- gint16 radius;
- gint16 spread;
+struct _GtkCssValue {
+ GTK_CSS_VALUE_BASE
+ guint inset :1;
- gboolean inset;
+ GtkCssValue *hoffset;
+ GtkCssValue *voffset;
+ GtkCssValue *radius;
+ GtkCssValue *spread;
- GdkRGBA color;
- GtkSymbolicColor *symbolic_color;
+ GtkCssValue *color;
};
-static void
-shadow_element_print (GtkShadowElement *element,
- GString *str)
-{
- gchar *color_str;
-
- if (element->inset)
- g_string_append (str, "inset ");
-
- g_string_append_printf (str, "%d %d ",
- (gint) element->hoffset,
- (gint) element->voffset);
-
- if (element->radius != 0)
- g_string_append_printf (str, "%d ", (gint) element->radius);
-
- if (element->spread != 0)
- g_string_append_printf (str, "%d ", (gint) element->spread);
-
- if (element->symbolic_color != NULL)
- color_str = gtk_symbolic_color_to_string (element->symbolic_color);
- else
- color_str = gdk_rgba_to_string (&element->color);
-
- g_string_append (str, color_str);
- g_free (color_str);
-}
+static GtkCssValue * gtk_css_shadow_value_new (GtkCssValue *hoffset,
+ GtkCssValue *voffset,
+ GtkCssValue *radius,
+ GtkCssValue *spread,
+ gboolean inset,
+ GtkCssValue *color);
static void
-shadow_element_free (GtkShadowElement *element)
+gtk_css_value_shadow_free (GtkCssValue *shadow)
{
- if (element->symbolic_color != NULL)
- gtk_symbolic_color_unref (element->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 (GtkShadowElement, element);
+ g_slice_free (GtkCssValue, shadow);
}
-static GtkShadowElement *
-shadow_element_new (gdouble hoffset,
- gdouble voffset,
- gdouble radius,
- gdouble spread,
- gboolean inset,
- GdkRGBA *color,
- GtkSymbolicColor *symbolic_color)
+static GtkCssValue *
+gtk_css_value_shadow_compute (GtkCssValue *shadow,
+ guint property_id,
+ GtkStyleProviderPrivate *provider,
+ GtkCssComputedValues *values,
+ GtkCssComputedValues *parent_values,
+ GtkCssDependencies *dependencies)
{
- GtkShadowElement *retval;
-
- retval = g_slice_new0 (GtkShadowElement);
-
- retval->hoffset = hoffset;
- retval->voffset = voffset;
- retval->radius = radius;
- retval->spread = spread;
- retval->inset = inset;
+ GtkCssValue *hoffset, *voffset, *radius, *spread, *color;
+ GtkCssDependencies child_deps;
- if (symbolic_color != NULL)
- retval->symbolic_color = gtk_symbolic_color_ref (symbolic_color);
+ 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);
- if (color != NULL)
- retval->color = *color;
+ 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);
- return retval;
-}
+ 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);
-/****************
- * GtkCssValue *
- ****************/
+ 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);
-struct _GtkCssValue {
- GTK_CSS_VALUE_BASE
- GList *elements;
-};
+ 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);
-static void
-gtk_css_value_shadow_free (GtkCssValue *shadow)
-{
- g_list_free_full (shadow->elements,
- (GDestroyNotify) shadow_element_free);
- g_slice_free (GtkCssValue, shadow);
+ 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)
{
- gint length;
- GList *l;
-
- length = g_list_length (shadow->elements);
+ _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 (length == 0)
+ if (_gtk_css_number_value_get (shadow->spread, 100) != 0)
{
- g_string_append (string, "none");
- return;
+ _gtk_css_value_print (shadow->spread, string);
+ g_string_append_c (string, ' ');
}
- shadow_element_print (shadow->elements->data, string);
+ _gtk_css_value_print (shadow->color, string);
- if (length == 1)
- return;
+ if (shadow->inset)
+ g_string_append (string, " inset");
- for (l = g_list_next (shadow->elements); l != NULL; l = l->next)
- {
- g_string_append (string, ", ");
- shadow_element_print (l->data, string);
- }
}
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 none_singleton = { >K_CSS_VALUE_SHADOW, 1, NULL };
+static GtkCssValue *
+gtk_css_shadow_value_new (GtkCssValue *hoffset,
+ GtkCssValue *voffset,
+ GtkCssValue *radius,
+ GtkCssValue *spread,
+ gboolean inset,
+ GtkCssValue *color)
+{
+ GtkCssValue *retval;
+
+ retval = _gtk_css_value_new (GtkCssValue, >K_CSS_VALUE_SHADOW);
+
+ retval->hoffset = hoffset;
+ retval->voffset = voffset;
+ retval->radius = radius;
+ retval->spread = spread;
+ retval->inset = inset;
+ retval->color = color;
+
+ return retval;
+}
GtkCssValue *
-_gtk_css_shadow_value_new_none (void)
+_gtk_css_shadow_value_new_for_transition (GtkCssValue *target)
{
- return _gtk_css_value_ref (&none_singleton);
+ GdkRGBA transparent = { 0, 0, 0, 0 };
+
+ g_return_val_if_fail (target->class == >K_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;
- GtkShadowElement *element;
- GtkCssValue *shadow;
+ enum {
+ HOFFSET,
+ VOFFSET,
+ RADIUS,
+ SPREAD,
+ COLOR,
+ N_VALUES
+ };
+ GtkCssValue *values[N_VALUES] = { NULL, };
+ gboolean inset;
guint i;
- if (_gtk_css_parser_try (parser, "none", TRUE))
- return _gtk_css_shadow_value_new_none ();
-
- shadow = _gtk_css_value_new (GtkCssValue, >K_CSS_VALUE_SHADOW);
+ 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++)
{
- have_inset = have_lengths = have_color = FALSE;
-
- for (i = 0; i < 3; 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");
- _gtk_css_value_unref (shadow);
- 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)
- {
- _gtk_css_value_unref (shadow);
- return NULL;
- }
- }
- }
-
- if (!have_color || !have_lengths)
- {
- _gtk_css_parser_error (parser, "Must specify at least color and offsets");
- _gtk_css_value_unref (shadow);
- return NULL;
- }
-
- element = shadow_element_new (hoffset, voffset,
- blur, spread, have_inset,
- NULL, color);
-
- shadow->elements = g_list_append (shadow->elements, element);
-
- gtk_symbolic_color_unref (color);
+ if (values[i])
+ _gtk_css_value_unref (values[i]);
+ }
+ 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))
+ {
+ double x, y;
+
+ cairo_get_current_point (cr, &x, &y);
+ cairo_move_to (blur_cr, x, y);
}
- while (_gtk_css_parser_try (parser, ",", TRUE));
- return shadow;
+ 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)
{
- GtkCssValue *resolved_shadow;
- GtkShadowElement *element, *resolved_element;
- GdkRGBA color;
- GList *l;
+ gdouble radius;
+ cairo_t *original_cr;
+ cairo_surface_t *surface;
- resolved_shadow = _gtk_css_value_new (GtkCssValue, >K_CSS_VALUE_SHADOW);
+ radius = _gtk_css_number_value_get (shadow->radius, 0);
+ if (radius == 0.0)
+ return cr;
- for (l = shadow->elements; l != NULL; l = l->next)
- {
- element = l->data;
-
- if (!_gtk_style_context_resolve_color (context,
- element->symbolic_color,
- &color))
- {
- _gtk_css_value_unref (resolved_shadow);
- return NULL;
- }
-
- resolved_element =
- shadow_element_new (element->hoffset, element->voffset,
- element->radius, element->spread, element->inset,
- &color, NULL);
-
- resolved_shadow->elements =
- g_list_append (resolved_shadow->elements, resolved_element);
- }
+ surface = cairo_get_target (cr);
+ original_cr = cairo_get_user_data (cr, &shadow_key);
+
+ /* Blur the surface. */
+ _gtk_cairo_blur_surface (surface, radius);
+
+ cairo_set_source_surface (original_cr, surface, 0, 0);
+ cairo_paint (original_cr);
+
+ cairo_destroy (cr);
+ cairo_surface_destroy (surface);
- return resolved_shadow;
+ return original_cr;
}
void
cairo_t *cr,
PangoLayout *layout)
{
- GList *l;
- GtkShadowElement *element;
+ g_return_if_fail (shadow->class == >K_CSS_VALUE_SHADOW);
if (!cairo_has_current_point (cr))
cairo_move_to (cr, 0, 0);
- /* render shadows starting from the last one,
- * and the others on top.
- */
- for (l = g_list_last (shadow->elements); l != NULL; l = l->prev)
- {
- element = l->data;
+ cairo_save (cr);
- cairo_save (cr);
+ cairo_rel_move_to (cr,
+ _gtk_css_number_value_get (shadow->hoffset, 0),
+ _gtk_css_number_value_get (shadow->voffset, 0));
- cairo_rel_move_to (cr, element->hoffset, element->voffset);
- gdk_cairo_set_source_rgba (cr, &element->color);
- _gtk_pango_fill_layout (cr, layout);
+ cr = gtk_css_shadow_value_start_drawing (shadow, cr);
- cairo_rel_move_to (cr, -element->hoffset, -element->voffset);
- cairo_restore (cr);
- }
+ gdk_cairo_set_source_rgba (cr, _gtk_css_rgba_value_get_rgba (shadow->color));
+ _gtk_pango_fill_layout (cr, layout);
+
+ 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);
}
void
_gtk_css_shadow_value_paint_icon (const GtkCssValue *shadow,
cairo_t *cr)
{
- GList *l;
- GtkShadowElement *element;
cairo_pattern_t *pattern;
- for (l = g_list_last (shadow->elements); l != NULL; l = l->prev)
- {
- element = l->data;
+ g_return_if_fail (shadow->class == >K_CSS_VALUE_SHADOW);
- cairo_save (cr);
- pattern = cairo_pattern_reference (cairo_get_source (cr));
- gdk_cairo_set_source_rgba (cr, &element->color);
+ cairo_save (cr);
+ pattern = cairo_pattern_reference (cairo_get_source (cr));
- cairo_translate (cr, element->hoffset, element->voffset);
- cairo_mask (cr, pattern);
+ cr = gtk_css_shadow_value_start_drawing (shadow, cr);
- cairo_restore (cr);
- cairo_pattern_destroy (pattern);
- }
+ 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);
}
void
gdouble radius,
gdouble progress)
{
- GtkShadowElement *element;
- GList *l;
+ g_return_if_fail (shadow->class == >K_CSS_VALUE_SHADOW);
- for (l = g_list_last (shadow->elements); l != NULL; l = l->prev)
- {
- element = l->data;
+ cairo_save (cr);
- cairo_save (cr);
+ cr = gtk_css_shadow_value_start_drawing (shadow, cr);
- cairo_translate (cr, element->hoffset, element->voffset);
- _gtk_theming_engine_paint_spinner (cr,
- radius, progress,
- &element->color);
+ 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,
+ _gtk_css_rgba_value_get_rgba (shadow->color));
- cairo_restore (cr);
- }
+ cr = gtk_css_shadow_value_finish_drawing (shadow, cr);
+
+ cairo_restore (cr);
}
void
cairo_t *cr,
const GtkRoundedBox *padding_box)
{
- GtkShadowElement *element;
- GtkRoundedBox box;
- GList *l;
+ GtkRoundedBox box, clip_box;
+ double spread, radius;
+
+ g_return_if_fail (shadow->class == >K_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);
- /* render shadows starting from the last one,
- * and the others on top.
- */
- for (l = g_list_last (shadow->elements); l != NULL; l = l->prev)
- {
- element = l->data;
+ box = *padding_box;
+ _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);
- if (!element->inset)
- continue;
+ clip_box = *padding_box;
+ radius = _gtk_css_number_value_get (shadow->radius, 0);
+ _gtk_rounded_box_shrink (&clip_box, -radius, -radius, -radius, -radius);
- box = *padding_box;
- _gtk_rounded_box_move (&box, element->hoffset, element->voffset);
- _gtk_rounded_box_shrink (&box,
- element->spread, element->spread,
- element->spread, element->spread);
+ cr = gtk_css_shadow_value_start_drawing (shadow, cr);
- _gtk_rounded_box_path (&box, cr);
- _gtk_rounded_box_clip_path (padding_box, cr);
+ cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+ _gtk_rounded_box_path (&box, cr);
+ _gtk_rounded_box_clip_path (&clip_box, cr);
- gdk_cairo_set_source_rgba (cr, &element->color);
- cairo_fill (cr);
- }
+ 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);
}