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
This commit is contained in:
Matthias Clasen 2021-01-26 17:40:34 -05:00
parent b6e7acfb90
commit 1258fcaaf4
11 changed files with 236 additions and 86 deletions

View File

@ -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[] = {

View File

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

View File

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

View File

@ -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 */

View File

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

View File

@ -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 >*/ {

View File

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

View File

@ -21,3 +21,11 @@ e {
f {
text-decoration-line: line-through;
}
g {
text-decoration-line: overline;
}
h {
text-decoration-line: underline overline;
}

View File

@ -21,3 +21,11 @@ e {
f {
text-decoration-line: line-through;
}
g {
text-decoration-line: overline;
}
h {
text-decoration-line: underline overline;
}

View File

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

View File

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