css: Add token parse for position values

And implement that parser completely. Now that we have calc(), this
actually works.
This commit is contained in:
Benjamin Otte
2016-03-18 20:13:20 +01:00
parent 90e423b34d
commit c767617d54
3 changed files with 267 additions and 1 deletions

View File

@@ -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)

View File

@@ -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);

View File

@@ -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),