[gtk/css-text-decoration] css: Fix text-decoration-line support




commit 1258fcaaf4d1148efb1b3f7a7dffba1b27f61c1c
Author: Matthias Clasen <mclasen redhat com>
Date:   Tue Jan 26 17:40:34 2021 -0500

    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

 gtk/gtkcssenumvalue.c                             | 145 ++++++++++++++--------
 gtk/gtkcssenumvalueprivate.h                      |   9 +-
 gtk/gtkcssshorthandpropertyimpl.c                 |  30 ++++-
 gtk/gtkcssstyle.c                                 |  17 ++-
 gtk/gtkcssstylepropertyimpl.c                     |  37 ++++--
 gtk/gtkcsstypesprivate.h                          |   8 +-
 testsuite/css/parser/meson.build                  |   2 +
 testsuite/css/parser/text-decoration-line.css     |   8 ++
 testsuite/css/parser/text-decoration-line.ref.css |   8 ++
 testsuite/css/parser/text-decoration.css          |  23 ++++
 testsuite/css/parser/text-decoration.ref.css      |  35 ++++++
 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;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]