From c767617d5405e5647329d324a3b7905c415efc9c Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Fri, 18 Mar 2016 20:13:20 +0100 Subject: [PATCH] css: Add token parse for position values And implement that parser completely. Now that we have calc(), this actually works. --- gtk/gtkcsspositionvalue.c | 256 +++++++++++++++++++++++++++++++ gtk/gtkcsspositionvalueprivate.h | 3 + gtk/gtkcssstylepropertyimpl.c | 9 +- 3 files changed, 267 insertions(+), 1 deletion(-) diff --git a/gtk/gtkcsspositionvalue.c b/gtk/gtkcsspositionvalue.c index 9102b16bb0..443a87063a 100644 --- a/gtk/gtkcsspositionvalue.c +++ b/gtk/gtkcsspositionvalue.c @@ -291,6 +291,262 @@ _gtk_css_position_value_try_parse (GtkCssParser *parser) return position_value_parse (parser, TRUE); } +gboolean +gtk_css_position_value_check_token (const GtkCssToken *token) +{ + return gtk_css_token_is_ident (token, "center") + || gtk_css_token_is_ident (token, "left") + || gtk_css_token_is_ident (token, "right") + || gtk_css_token_is_ident (token, "top") + || gtk_css_token_is_ident (token, "bottom") + || gtk_css_number_value_check_token (token); +} + +typedef enum { + NONE = 0, + CENTER, + LEFT, + RIGHT, + TOP, + BOTTOM +} Keyword; + +static Keyword +get_keyword (const GtkCssToken *token) +{ + if (gtk_css_token_is_ident (token, "center")) + return CENTER; + else if (gtk_css_token_is_ident (token, "left")) + return LEFT; + else if (gtk_css_token_is_ident (token, "right")) + return RIGHT; + else if (gtk_css_token_is_ident (token, "top")) + return TOP; + else if (gtk_css_token_is_ident (token, "bottom")) + return BOTTOM; + else + return NONE; +} + +static GtkCssValue * +value_from_keyword (Keyword keyword, + GtkCssValue *value) +{ + switch (keyword) + { + default: + case NONE: + g_assert_not_reached (); + return NULL; + case CENTER: + g_assert (value == NULL); + return _gtk_css_number_value_new (50, GTK_CSS_PERCENT); + case LEFT: + case TOP: + if (value) + return value; + else + return _gtk_css_number_value_new (0, GTK_CSS_PERCENT); + case RIGHT: + case BOTTOM: + if (value == NULL) + return _gtk_css_number_value_new (100, GTK_CSS_PERCENT); + else + { + GtkCssValue *mult = gtk_css_number_value_multiply (value, -1); + GtkCssValue *hundred = _gtk_css_number_value_new (100, GTK_CSS_PERCENT); + GtkCssValue *sum = gtk_css_number_value_add (hundred, mult); + + _gtk_css_value_unref (mult); + _gtk_css_value_unref (hundred); + _gtk_css_value_unref (value); + return sum; + } + } +} + +static gboolean +keywords_compatible (Keyword a, Keyword b) +{ + if ((a == LEFT || a == RIGHT) && (b == LEFT || b == RIGHT)) + return FALSE; + if ((a == TOP || a == BOTTOM) && (b == TOP || b == BOTTOM)) + return FALSE; + + return TRUE; +} + +static gboolean +keywords_need_swap (Keyword x, Keyword y) +{ + /* NB: We assume the keywords are compatible here */ + return x == TOP || x == BOTTOM + || y == LEFT || y == RIGHT; +} + +GtkCssValue * +gtk_css_position_value_token_parse (GtkCssTokenSource *source) +{ + const GtkCssToken *token; + GtkCssValue *x, *y; + Keyword keyword; + + token = gtk_css_token_source_get_token (source); + + keyword = get_keyword (token); + if (keyword == NONE) + { + /* NUMBER ... */ + x = gtk_css_number_value_token_parse (source, GTK_CSS_PARSE_PERCENT | GTK_CSS_PARSE_LENGTH); + if (x == NULL) + return NULL; + + token = gtk_css_token_source_get_token (source); + keyword = get_keyword (token); + if (keyword == NONE) + { + if (gtk_css_number_value_check_token (token)) + { + /* NUMBER NUMBER */ + y = gtk_css_number_value_token_parse (source, GTK_CSS_PARSE_PERCENT | GTK_CSS_PARSE_LENGTH); + if (y == NULL) + { + _gtk_css_value_unref (x); + return NULL; + } + } + else + { + /* NUMBER */ + y = value_from_keyword (CENTER, NULL); + } + } + else if (keyword == LEFT || keyword == RIGHT) + { + gtk_css_token_source_error (source, "\"left\" and \"right\" may not follow a number"); + gtk_css_token_source_consume_all (source); + _gtk_css_value_unref (x); + return NULL; + } + else + { + /* NUMBER KEYWORD */ + y = value_from_keyword (keyword, NULL); + } + } + else + { + /* KEYWORD ... */ + Keyword keyword2; + GtkCssValue *value; + + gtk_css_token_source_consume_token (source); + token = gtk_css_token_source_get_token (source); + keyword2 = get_keyword (token); + if (keyword2 != NONE) + { + /* KEYWORD KEYWORD ... */ + if (!keywords_compatible (keyword, keyword2)) + { + gtk_css_token_source_error (source, "Two keywords for same axis"); + gtk_css_token_source_consume_all (source); + return NULL; + } + gtk_css_token_source_consume_token (source); + token = gtk_css_token_source_get_token (source); + if (keyword2 != CENTER && gtk_css_number_value_check_token (token)) + { + /* KEYWORD KEYWORD NUMBER */ + value = gtk_css_number_value_token_parse (source, GTK_CSS_PARSE_PERCENT | GTK_CSS_PARSE_LENGTH); + if (value == NULL) + return NULL; + } + else + value = NULL; + + if (keywords_need_swap (keyword, keyword2)) + { + x = value_from_keyword (keyword2, value); + y = value_from_keyword (keyword, NULL); + } + else + { + x = value_from_keyword (keyword, NULL); + y = value_from_keyword (keyword2, value); + } + } + else + { + if (gtk_css_number_value_check_token (token)) + { + /* KEYWORD NUMBER ... */ + x = gtk_css_number_value_token_parse (source, GTK_CSS_PARSE_PERCENT | GTK_CSS_PARSE_LENGTH); + if (x == NULL) + return NULL; + + token = gtk_css_token_source_get_token (source); + keyword2 = get_keyword (token); + if (keyword2 == NONE) + { + /* KEYWORD NUMBER */ + if (keyword == TOP || keyword == BOTTOM) + { + x = value_from_keyword (LEFT, x); + y = value_from_keyword (keyword, NULL); + } + else + { + y = value_from_keyword (LEFT, x); + x = value_from_keyword (keyword, NULL); + } + } + else + { + /* KEYWORD NUMBER KEYWORD ... */ + if (!keywords_compatible (keyword, keyword2)) + { + gtk_css_token_source_error (source, "Two keywords for same axis"); + gtk_css_token_source_consume_all (source); + return NULL; + } + + gtk_css_token_source_consume_token (source); + token = gtk_css_token_source_get_token (source); + if (keyword2 != CENTER && gtk_css_number_value_check_token (token)) + { + /* KEYWORD NUMBER KEYWORD NUMBER */ + y = gtk_css_number_value_token_parse (source, GTK_CSS_PARSE_PERCENT | GTK_CSS_PARSE_LENGTH); + if (y == NULL) + return NULL; + } + else + y = NULL; + + if (keywords_need_swap (keyword, keyword2)) + { + value = value_from_keyword (keyword2, y); + y = value_from_keyword (keyword, x); + x = value; + } + else + { + x = value_from_keyword (keyword, x); + y = value_from_keyword (keyword, y); + } + } + } + else + { + /* KEYWORD */ + x = value_from_keyword (keyword, NULL); + y = value_from_keyword (CENTER, NULL); + } + } + } + + return _gtk_css_position_value_new (x, y); +} + double _gtk_css_position_value_get_x (const GtkCssValue *position, double one_hundred_percent) diff --git a/gtk/gtkcsspositionvalueprivate.h b/gtk/gtkcsspositionvalueprivate.h index d1d113bb13..ab43e79df8 100644 --- a/gtk/gtkcsspositionvalueprivate.h +++ b/gtk/gtkcsspositionvalueprivate.h @@ -21,6 +21,7 @@ #define __GTK_CSS_POSITION_VALUE_PRIVATE_H__ #include "gtkcssparserprivate.h" +#include "gtkcsstokensourceprivate.h" #include "gtkcssvalueprivate.h" G_BEGIN_DECLS @@ -29,6 +30,8 @@ GtkCssValue * _gtk_css_position_value_new (GtkCssValue *x GtkCssValue *y); GtkCssValue * _gtk_css_position_value_parse (GtkCssParser *parser); GtkCssValue * _gtk_css_position_value_try_parse (GtkCssParser *parser); +gboolean gtk_css_position_value_check_token (const GtkCssToken *token); +GtkCssValue * gtk_css_position_value_token_parse (GtkCssTokenSource *source); double _gtk_css_position_value_get_x (const GtkCssValue *position, double one_hundred_percent); diff --git a/gtk/gtkcssstylepropertyimpl.c b/gtk/gtkcssstylepropertyimpl.c index 1edb142b4b..abd998799d 100644 --- a/gtk/gtkcssstylepropertyimpl.c +++ b/gtk/gtkcssstylepropertyimpl.c @@ -1444,6 +1444,13 @@ background_position_parse (GtkCssStyleProperty *property, return _gtk_css_array_value_parse (parser, _gtk_css_position_value_parse); } +static GtkCssValue * +background_position_token_parse (GtkCssTokenSource *source, + GtkCssStyleProperty *property) +{ + return gtk_css_array_value_token_parse (source, gtk_css_position_value_token_parse); +} + static GtkCssValue * icon_theme_value_parse (GtkCssStyleProperty *property, GtkCssParser *parser) @@ -1965,7 +1972,7 @@ _gtk_css_style_property_init_properties (void) GTK_STYLE_PROPERTY_ANIMATED, GTK_CSS_AFFECTS_BACKGROUND, background_position_parse, - gtk_css_style_property_token_parse_default, + background_position_token_parse, NULL, NULL, _gtk_css_array_value_new (_gtk_css_position_value_new (_gtk_css_number_value_new (0, GTK_CSS_PERCENT),