summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2021-01-26 17:40:34 -0500
committerMatthias Clasen <mclasen@redhat.com>2021-01-26 17:53:14 -0500
commit1258fcaaf4d1148efb1b3f7a7dffba1b27f61c1c (patch)
treecd89365dc3431db002dc676a40b8c03df23a7493
parentb6e7acfb9008979ef9b14f4e7a74f01701148b78 (diff)
downloadgtk+-1258fcaaf4d1148efb1b3f7a7dffba1b27f61c1c.tar.gz
css: Fix text-decoration-line support
This property needs to be treated as flags, not as enum, since it should be possible to specify more than one value, e.g. text-decoration-line: underline overline; Tests included. Fixes: #3621
-rw-r--r--gtk/gtkcssenumvalue.c145
-rw-r--r--gtk/gtkcssenumvalueprivate.h9
-rw-r--r--gtk/gtkcssshorthandpropertyimpl.c30
-rw-r--r--gtk/gtkcssstyle.c17
-rw-r--r--gtk/gtkcssstylepropertyimpl.c37
-rw-r--r--gtk/gtkcsstypesprivate.h8
-rw-r--r--testsuite/css/parser/meson.build2
-rw-r--r--testsuite/css/parser/text-decoration-line.css8
-rw-r--r--testsuite/css/parser/text-decoration-line.ref.css8
-rw-r--r--testsuite/css/parser/text-decoration.css23
-rw-r--r--testsuite/css/parser/text-decoration.ref.css35
11 files changed, 236 insertions, 86 deletions
diff --git a/gtk/gtkcssenumvalue.c b/gtk/gtkcssenumvalue.c
index 6a9b4bfec7..d28daf5ac2 100644
--- a/gtk/gtkcssenumvalue.c
+++ b/gtk/gtkcssenumvalue.c
@@ -545,58 +545,6 @@ _gtk_css_font_stretch_value_get (const GtkCssValue *value)
return value->value;
}
-/* GtkTextDecorationLine */
-
-static const GtkCssValueClass GTK_CSS_VALUE_TEXT_DECORATION_LINE = {
- "GtkCssTextDecorationLineValue",
- gtk_css_value_enum_free,
- gtk_css_value_enum_compute,
- gtk_css_value_enum_equal,
- gtk_css_value_enum_transition,
- NULL,
- NULL,
- gtk_css_value_enum_print
-};
-
-static GtkCssValue text_decoration_line_values[] = {
- { &GTK_CSS_VALUE_TEXT_DECORATION_LINE, 1, TRUE, GTK_CSS_TEXT_DECORATION_LINE_NONE, "none" },
- { &GTK_CSS_VALUE_TEXT_DECORATION_LINE, 1, TRUE, GTK_CSS_TEXT_DECORATION_LINE_UNDERLINE, "underline" },
- { &GTK_CSS_VALUE_TEXT_DECORATION_LINE, 1, TRUE, GTK_CSS_TEXT_DECORATION_LINE_OVERLINE, "overline" },
- { &GTK_CSS_VALUE_TEXT_DECORATION_LINE, 1, TRUE, GTK_CSS_TEXT_DECORATION_LINE_LINE_THROUGH, "line-through" },
-};
-
-GtkCssValue *
-_gtk_css_text_decoration_line_value_new (GtkTextDecorationLine line)
-{
- g_return_val_if_fail (line < G_N_ELEMENTS (text_decoration_line_values), NULL);
-
- return _gtk_css_value_ref (&text_decoration_line_values[line]);
-}
-
-GtkCssValue *
-_gtk_css_text_decoration_line_value_try_parse (GtkCssParser *parser)
-{
- guint i;
-
- g_return_val_if_fail (parser != NULL, NULL);
-
- for (i = 0; i < G_N_ELEMENTS (text_decoration_line_values); i++)
- {
- if (gtk_css_parser_try_ident (parser, text_decoration_line_values[i].name))
- return _gtk_css_value_ref (&text_decoration_line_values[i]);
- }
-
- return NULL;
-}
-
-GtkTextDecorationLine
-_gtk_css_text_decoration_line_value_get (const GtkCssValue *value)
-{
- g_return_val_if_fail (value->class == &GTK_CSS_VALUE_TEXT_DECORATION_LINE, GTK_CSS_TEXT_DECORATION_LINE_NONE);
-
- return value->value;
-}
-
/* GtkTextDecorationStyle */
static const GtkCssValueClass GTK_CSS_VALUE_TEXT_DECORATION_STYLE = {
@@ -1204,6 +1152,99 @@ gtk_css_value_flags_print (const FlagsValue *values,
}
}
+/* GtkTextDecorationLine */
+
+static FlagsValue text_decoration_line_values[] = {
+ { GTK_CSS_TEXT_DECORATION_LINE_NONE, "none" },
+ { GTK_CSS_TEXT_DECORATION_LINE_UNDERLINE, "underline" },
+ { GTK_CSS_TEXT_DECORATION_LINE_OVERLINE, "overline" },
+ { GTK_CSS_TEXT_DECORATION_LINE_LINE_THROUGH, "line-through" },
+};
+
+static void
+gtk_css_text_decoration_line_value_print (const GtkCssValue *value,
+ GString *string)
+{
+ gtk_css_value_flags_print (text_decoration_line_values,
+ G_N_ELEMENTS (text_decoration_line_values),
+ value, string);
+}
+
+static const GtkCssValueClass GTK_CSS_VALUE_TEXT_DECORATION_LINE = {
+ "GtkCssTextDecorationLine",
+ gtk_css_value_enum_free,
+ gtk_css_value_enum_compute,
+ gtk_css_value_flags_equal,
+ gtk_css_value_enum_transition,
+ NULL,
+ NULL,
+ gtk_css_text_decoration_line_value_print
+};
+
+static gboolean
+text_decoration_line_is_valid (GtkTextDecorationLine line)
+{
+ if ((line & GTK_CSS_TEXT_DECORATION_LINE_NONE) &&
+ (line != GTK_CSS_TEXT_DECORATION_LINE_NONE))
+ return FALSE;
+
+ return TRUE;
+}
+
+GtkCssValue *
+_gtk_css_text_decoration_line_value_new (GtkTextDecorationLine line)
+{
+ GtkCssValue *value;
+
+ if (!text_decoration_line_is_valid (line))
+ return NULL;
+
+ value = _gtk_css_value_new (GtkCssValue, &GTK_CSS_VALUE_TEXT_DECORATION_LINE);
+ value->value = line;
+ value->name = NULL;
+ value->is_computed = TRUE;
+
+ return value;
+}
+
+GtkTextDecorationLine
+_gtk_css_text_decoration_line_try_parse_one (GtkCssParser *parser,
+ GtkTextDecorationLine base)
+{
+ guint i;
+ GtkTextDecorationLine value = 0;
+
+ g_return_val_if_fail (parser != NULL, 0);
+
+ for (i = 0; i < G_N_ELEMENTS (text_decoration_line_values); i++)
+ {
+ if (gtk_css_parser_try_ident (parser, text_decoration_line_values[i].name))
+ {
+ value = text_decoration_line_values[i].value;
+ break;
+ }
+ }
+
+ if (value == 0)
+ return base; /* not parsing this value */
+
+ if ((base | value) == base)
+ return 0; /* repeated value */
+
+ if (!text_decoration_line_is_valid (base | value))
+ return 0; /* bad combination */
+
+ return base | value;
+}
+
+GtkTextDecorationLine
+_gtk_css_text_decoration_line_value_get (const GtkCssValue *value)
+{
+ g_return_val_if_fail (value->class == &GTK_CSS_VALUE_TEXT_DECORATION_LINE, GTK_CSS_TEXT_DECORATION_LINE_NONE);
+
+ return value->value;
+}
+
/* GtkCssFontVariantLigature */
static FlagsValue font_variant_ligature_values[] = {
diff --git a/gtk/gtkcssenumvalueprivate.h b/gtk/gtkcssenumvalueprivate.h
index 7ac181b7a9..a102a8650b 100644
--- a/gtk/gtkcssenumvalueprivate.h
+++ b/gtk/gtkcssenumvalueprivate.h
@@ -54,11 +54,12 @@ GtkCssValue * _gtk_css_font_stretch_value_new (PangoStretch stretc
GtkCssValue * _gtk_css_font_stretch_value_try_parse (GtkCssParser *parser);
PangoStretch _gtk_css_font_stretch_value_get (const GtkCssValue *value);
-GtkCssValue * _gtk_css_text_decoration_line_value_new (GtkTextDecorationLine line);
-GtkCssValue * _gtk_css_text_decoration_line_value_try_parse (GtkCssParser *parser);
-GtkTextDecorationLine _gtk_css_text_decoration_line_value_get (const GtkCssValue *value);
+GtkCssValue * _gtk_css_text_decoration_line_value_new (GtkTextDecorationLine line);
+GtkTextDecorationLine _gtk_css_text_decoration_line_try_parse_one (GtkCssParser *parser,
+ GtkTextDecorationLine base);
+GtkTextDecorationLine _gtk_css_text_decoration_line_value_get (const GtkCssValue *value);
-GtkCssValue * _gtk_css_text_decoration_style_value_new (GtkTextDecorationStyle style);
+GtkCssValue * _gtk_css_text_decoration_style_value_new (GtkTextDecorationStyle style);
GtkCssValue * _gtk_css_text_decoration_style_value_try_parse (GtkCssParser *parser);
GtkTextDecorationStyle _gtk_css_text_decoration_style_value_get (const GtkCssValue *value);
diff --git a/gtk/gtkcssshorthandpropertyimpl.c b/gtk/gtkcssshorthandpropertyimpl.c
index 4a9b97fd07..5908904d25 100644
--- a/gtk/gtkcssshorthandpropertyimpl.c
+++ b/gtk/gtkcssshorthandpropertyimpl.c
@@ -865,16 +865,26 @@ parse_text_decoration (GtkCssShorthandProperty *shorthand,
GtkCssValue **values,
GtkCssParser *parser)
{
+ GtkTextDecorationLine line = 0;
+
do
{
- if (values[0] == NULL &&
- (values[0] = _gtk_css_text_decoration_line_value_try_parse (parser)))
+ GtkTextDecorationLine parsed_line;
+
+ parsed_line = _gtk_css_text_decoration_line_try_parse_one (parser, line);
+
+ if (parsed_line == 0 && line != 0)
{
- if (values[0] == NULL)
- return FALSE;
+ gtk_css_parser_error_value (parser, "Invalid combination of text-decoration-line values");
+ return FALSE;
+ }
+
+ if (parsed_line != line)
+ {
+ line = parsed_line;
}
else if (values[1] == NULL &&
- (values[1] = _gtk_css_text_decoration_style_value_try_parse (parser)))
+ (values[1] = _gtk_css_text_decoration_style_value_try_parse (parser)))
{
if (values[1] == NULL)
return FALSE;
@@ -895,6 +905,16 @@ parse_text_decoration (GtkCssShorthandProperty *shorthand,
}
while (!value_is_done_parsing (parser));
+ if (line != 0)
+ {
+ values[0] = _gtk_css_text_decoration_line_value_new (line);
+ if (values[0] == NULL)
+ {
+ gtk_css_parser_error_value (parser, "Invalid combination of text-decoration-line values");
+ return FALSE;
+ }
+ }
+
return TRUE;
}
diff --git a/gtk/gtkcssstyle.c b/gtk/gtkcssstyle.c
index ee6f9c44c5..91830266a5 100644
--- a/gtk/gtkcssstyle.c
+++ b/gtk/gtkcssstyle.c
@@ -451,32 +451,29 @@ gtk_css_style_get_pango_attributes (GtkCssStyle *style)
? style->font_variant->text_decoration_color
: style->core->color);
- switch (decoration_line)
+ if (decoration_line & GTK_CSS_TEXT_DECORATION_LINE_UNDERLINE)
{
- case GTK_CSS_TEXT_DECORATION_LINE_UNDERLINE:
attrs = add_pango_attr (attrs, pango_attr_underline_new (get_pango_underline_from_style (decoration_style)));
if (!gdk_rgba_equal (color, decoration_color))
attrs = add_pango_attr (attrs, pango_attr_underline_color_new (decoration_color->red * 65535. + 0.5,
decoration_color->green * 65535. + 0.5,
decoration_color->blue * 65535. + 0.5));
- break;
- case GTK_CSS_TEXT_DECORATION_LINE_OVERLINE:
+ }
+ if (decoration_line & GTK_CSS_TEXT_DECORATION_LINE_OVERLINE)
+ {
attrs = add_pango_attr (attrs, pango_attr_overline_new (get_pango_overline_from_style (decoration_style)));
if (!gdk_rgba_equal (color, decoration_color))
attrs = add_pango_attr (attrs, pango_attr_overline_color_new (decoration_color->red * 65535. + 0.5,
decoration_color->green * 65535. + 0.5,
decoration_color->blue * 65535. + 0.5));
- break;
- case GTK_CSS_TEXT_DECORATION_LINE_LINE_THROUGH:
+ }
+ if (decoration_line & GTK_CSS_TEXT_DECORATION_LINE_LINE_THROUGH)
+ {
attrs = add_pango_attr (attrs, pango_attr_strikethrough_new (TRUE));
if (!gdk_rgba_equal (color, decoration_color))
attrs = add_pango_attr (attrs, pango_attr_strikethrough_color_new (decoration_color->red * 65535. + 0.5,
decoration_color->green * 65535. + 0.5,
decoration_color->blue * 65535. + 0.5));
- break;
- case GTK_CSS_TEXT_DECORATION_LINE_NONE:
- default:
- break;
}
/* letter-spacing */
diff --git a/gtk/gtkcssstylepropertyimpl.c b/gtk/gtkcssstylepropertyimpl.c
index 3eb4ac7c5c..8e5d4bf796 100644
--- a/gtk/gtkcssstylepropertyimpl.c
+++ b/gtk/gtkcssstylepropertyimpl.c
@@ -317,14 +317,38 @@ parse_letter_spacing (GtkCssStyleProperty *property,
return _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_LENGTH);
}
+static gboolean
+value_is_done_parsing (GtkCssParser *parser)
+{
+ return gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF) ||
+ gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_COMMA) ||
+ gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_SEMICOLON) ||
+ gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_CLOSE_CURLY);
+}
+
static GtkCssValue *
parse_text_decoration_line (GtkCssStyleProperty *property,
GtkCssParser *parser)
{
- GtkCssValue *value = _gtk_css_text_decoration_line_value_try_parse (parser);
+ GtkCssValue *value = NULL;
+ GtkTextDecorationLine line;
+
+ line = 0;
+ do {
+ GtkTextDecorationLine parsed;
+
+ parsed = _gtk_css_text_decoration_line_try_parse_one (parser, line);
+ if (parsed == 0 || parsed == line)
+ {
+ gtk_css_parser_error_syntax (parser, "Not a valid value");
+ return NULL;
+ }
+ line = parsed;
+ } while (!value_is_done_parsing (parser));
+ value = _gtk_css_text_decoration_line_value_new (line);
if (value == NULL)
- gtk_css_parser_error_syntax (parser, "unknown text decoration line value");
+ gtk_css_parser_error_syntax (parser, "Invalid combination of values");
return value;
}
@@ -353,15 +377,6 @@ parse_font_kerning (GtkCssStyleProperty *property,
return value;
}
-static gboolean
-value_is_done_parsing (GtkCssParser *parser)
-{
- return gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF) ||
- gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_COMMA) ||
- gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_SEMICOLON) ||
- gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_CLOSE_CURLY);
-}
-
static GtkCssValue *
parse_font_variant_ligatures (GtkCssStyleProperty *property,
GtkCssParser *parser)
diff --git a/gtk/gtkcsstypesprivate.h b/gtk/gtkcsstypesprivate.h
index efb54a5561..35ae56a41c 100644
--- a/gtk/gtkcsstypesprivate.h
+++ b/gtk/gtkcsstypesprivate.h
@@ -325,10 +325,10 @@ typedef enum /*< skip >*/ {
} GtkCssFontSize;
typedef enum /*< skip >*/ {
- GTK_CSS_TEXT_DECORATION_LINE_NONE,
- GTK_CSS_TEXT_DECORATION_LINE_UNDERLINE,
- GTK_CSS_TEXT_DECORATION_LINE_OVERLINE,
- GTK_CSS_TEXT_DECORATION_LINE_LINE_THROUGH
+ GTK_CSS_TEXT_DECORATION_LINE_NONE = 1 << 0,
+ GTK_CSS_TEXT_DECORATION_LINE_UNDERLINE = 1 << 1,
+ GTK_CSS_TEXT_DECORATION_LINE_OVERLINE = 1 << 2,
+ GTK_CSS_TEXT_DECORATION_LINE_LINE_THROUGH = 1 << 3
} GtkTextDecorationLine;
typedef enum /*< skip >*/ {
diff --git a/testsuite/css/parser/meson.build b/testsuite/css/parser/meson.build
index f70753c2b5..b398251450 100644
--- a/testsuite/css/parser/meson.build
+++ b/testsuite/css/parser/meson.build
@@ -423,6 +423,8 @@ test_data = [
'string-values.css',
'string-values.ref.css',
'test.png',
+ 'text-decoration.css',
+ 'text-decoration.ref.css',
'text-decoration-color.css',
'text-decoration-color.ref.css',
'text-decoration-line.css',
diff --git a/testsuite/css/parser/text-decoration-line.css b/testsuite/css/parser/text-decoration-line.css
index 16311eb23c..1725688a8c 100644
--- a/testsuite/css/parser/text-decoration-line.css
+++ b/testsuite/css/parser/text-decoration-line.css
@@ -21,3 +21,11 @@ e {
f {
text-decoration-line: line-through;
}
+
+g {
+ text-decoration-line: overline;
+}
+
+h {
+ text-decoration-line: underline overline;
+}
diff --git a/testsuite/css/parser/text-decoration-line.ref.css b/testsuite/css/parser/text-decoration-line.ref.css
index 16311eb23c..1725688a8c 100644
--- a/testsuite/css/parser/text-decoration-line.ref.css
+++ b/testsuite/css/parser/text-decoration-line.ref.css
@@ -21,3 +21,11 @@ e {
f {
text-decoration-line: line-through;
}
+
+g {
+ text-decoration-line: overline;
+}
+
+h {
+ text-decoration-line: underline overline;
+}
diff --git a/testsuite/css/parser/text-decoration.css b/testsuite/css/parser/text-decoration.css
new file mode 100644
index 0000000000..3496b0713a
--- /dev/null
+++ b/testsuite/css/parser/text-decoration.css
@@ -0,0 +1,23 @@
+a {
+ text-decoration: underline;
+}
+
+b {
+ text-decoration: wavy;
+}
+
+c {
+ text-decoration: red;
+}
+
+d {
+ text-decoration: red wavy;
+}
+
+e {
+ text-decoration: none;
+}
+
+f {
+ text-decoration: red underline wavy overline;
+}
diff --git a/testsuite/css/parser/text-decoration.ref.css b/testsuite/css/parser/text-decoration.ref.css
new file mode 100644
index 0000000000..7369332ce3
--- /dev/null
+++ b/testsuite/css/parser/text-decoration.ref.css
@@ -0,0 +1,35 @@
+a {
+ text-decoration-color: initial;
+ text-decoration-line: underline;
+ text-decoration-style: initial;
+}
+
+b {
+ text-decoration-color: initial;
+ text-decoration-line: initial;
+ text-decoration-style: wavy;
+}
+
+c {
+ text-decoration-color: rgb(255,0,0);
+ text-decoration-line: initial;
+ text-decoration-style: initial;
+}
+
+d {
+ text-decoration-color: rgb(255,0,0);
+ text-decoration-line: initial;
+ text-decoration-style: wavy;
+}
+
+e {
+ text-decoration-color: initial;
+ text-decoration-line: none;
+ text-decoration-style: initial;
+}
+
+f {
+ text-decoration-color: rgb(255,0,0);
+ text-decoration-line: underline overline;
+ text-decoration-style: wavy;
+}