From 27bbfe9d8003a35e9811515767a2a90fea677980 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Wed, 30 Mar 2016 16:49:50 +0200 Subject: [PATCH] cssimage: Add a token parser for radial gradients --- gtk/gtkcssimageradial.c | 205 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) diff --git a/gtk/gtkcssimageradial.c b/gtk/gtkcssimageradial.c index bc137db7da..f690da8279 100644 --- a/gtk/gtkcssimageradial.c +++ b/gtk/gtkcssimageradial.c @@ -386,6 +386,210 @@ gtk_css_image_radial_parse (GtkCssImage *image, return TRUE; } +static gboolean +gtk_css_image_radial_token_parse_size (GtkCssImageRadial *radial, + GtkCssTokenSource *source, + gboolean *shape_is_known, + gboolean *has_size) +{ + static const struct { + const char *name; + guint value; + } names[] = { + { "closest-side", GTK_CSS_CLOSEST_SIDE }, + { "farthest-side", GTK_CSS_FARTHEST_SIDE }, + { "closest-corner", GTK_CSS_CLOSEST_CORNER }, + { "farthest-corner", GTK_CSS_FARTHEST_CORNER } + }; + const GtkCssToken *token; + guint i; + + token = gtk_css_token_source_get_token (source); + for (i = 0; i < G_N_ELEMENTS (names); i++) + { + if (gtk_css_token_is_ident (token, names[i].name)) + { + radial->size = names[i].value; + gtk_css_token_source_consume_token (source); + *has_size = TRUE; + return TRUE; + } + } + + if (!gtk_css_number_value_check_token (token)) + { + radial->size = GTK_CSS_FARTHEST_CORNER; + return TRUE; + } + + *has_size = TRUE; + radial->sizes[0] = gtk_css_number_value_token_parse (source, + GTK_CSS_PARSE_LENGTH | + (!*shape_is_known || !radial->circle ? GTK_CSS_PARSE_PERCENT : 0)); + if (radial->sizes[0] == NULL) + return FALSE; + if (gtk_css_number_value_has_percent (radial->sizes[0])) + { + *shape_is_known = TRUE; + radial->circle = FALSE; + } + if (!*shape_is_known) + { + *shape_is_known = TRUE; + token = gtk_css_token_source_get_token (source); + radial->circle = !gtk_css_number_value_check_token (token); + } + + if (radial->circle) + return TRUE; + + radial->sizes[1] = gtk_css_number_value_token_parse (source, + GTK_CSS_PARSE_LENGTH | GTK_CSS_PARSE_PERCENT); + if (radial->sizes[1] == NULL) + return FALSE; + + return TRUE; +} + +static guint +gtk_css_image_radial_token_parse_argument (GtkCssTokenSource *source, + guint arg, + gpointer data) +{ + GtkCssImageRadial *radial = data; + GtkCssImageRadialColorStop stop; + const GtkCssToken *token; + + if (arg == 0) + { + gboolean has_shape = FALSE, shape_is_known = FALSE, has_size = FALSE; + + token = gtk_css_token_source_get_token (source); + if (gtk_css_token_is_ident (token, "ellipse")) + { + shape_is_known = has_shape = TRUE; + gtk_css_token_source_consume_token (source); + } + else if (gtk_css_token_is_ident (token, "circle")) + { + radial->circle = TRUE; + shape_is_known = has_shape = TRUE; + gtk_css_token_source_consume_token (source); + } + + if (!gtk_css_image_radial_token_parse_size (radial, source, &shape_is_known, &has_size)) + return 0; + + if (!has_shape) + { + token = gtk_css_token_source_get_token (source); + if (gtk_css_token_is_ident (token, "ellipse")) + { + if (shape_is_known && radial->circle) + { + gtk_css_token_source_error (source, "Specified size not allowed for ellipse"); + gtk_css_token_source_consume_all (source); + return 0; + } + shape_is_known = TRUE; + has_shape = TRUE; + gtk_css_token_source_consume_token (source); + } + else if (gtk_css_token_is_ident (token, "circle")) + { + if (shape_is_known && !radial->circle) + { + gtk_css_token_source_error (source, "Specified size not allowed for circle"); + gtk_css_token_source_consume_all (source); + return 0; + } + radial->circle = TRUE; + shape_is_known = TRUE; + has_shape = TRUE; + gtk_css_token_source_consume_token (source); + } + } + if (!shape_is_known) + { + g_assert (radial->size != GTK_CSS_EXPLICIT_SIZE); + radial->circle = TRUE; + } + + token = gtk_css_token_source_get_token (source); + if (gtk_css_token_is_ident (token, "at")) + { + gtk_css_token_source_consume_token (source); + + radial->position = gtk_css_position_value_token_parse (source); + if (radial->position == NULL) + return 0; + return 1; + } + else + { + radial->position = _gtk_css_position_value_new (_gtk_css_number_value_new (50, GTK_CSS_PERCENT), + _gtk_css_number_value_new (50, GTK_CSS_PERCENT)); + } + + if (has_shape || has_size) + return 1; + } + + stop.color = gtk_css_color_value_token_parse (source); + if (stop.color == NULL) + return 0; + + token = gtk_css_token_source_get_token (source); + if (gtk_css_number_value_check_token (token)) + { + stop.offset = gtk_css_number_value_token_parse (source, + GTK_CSS_PARSE_PERCENT + | GTK_CSS_PARSE_LENGTH); + if (stop.offset == NULL) + { + _gtk_css_value_unref (stop.color); + return FALSE; + } + } + else + { + stop.offset = NULL; + } + + g_array_append_val (radial->stops, stop); + /* We consume the optional side argument here, too. */ + return arg == 0 ? 2 : 1; +} + +static gboolean +gtk_css_image_radial_token_parse (GtkCssImage *image, + GtkCssTokenSource *source) +{ + GtkCssImageRadial *radial = GTK_CSS_IMAGE_RADIAL (image); + const GtkCssToken *token; + + token = gtk_css_token_source_get_token (source); + if (gtk_css_token_is_function (token, "radial-gradient")) + { + /* nothing to do here */ + } + else if (gtk_css_token_is_function (token, "repeating-radial-gradient")) + { + radial->repeating = TRUE; + } + else + { + gtk_css_token_source_error (source, "Expected 'radial-gradient('"); + gtk_css_token_source_consume_all (source); + return FALSE; + } + + return gtk_css_token_source_consume_function (source, + 3, G_MAXUINT, + gtk_css_image_radial_token_parse_argument, + image); +} + static void gtk_css_image_radial_print (GtkCssImage *image, GString *string) @@ -662,6 +866,7 @@ _gtk_css_image_radial_class_init (GtkCssImageRadialClass *klass) image_class->draw = gtk_css_image_radial_draw; image_class->parse = gtk_css_image_radial_parse; + image_class->token_parse = gtk_css_image_radial_token_parse; image_class->print = gtk_css_image_radial_print; image_class->compute = gtk_css_image_radial_compute; image_class->transition = gtk_css_image_radial_transition;