[gtk+] css: Add support for sums to calc()



commit 65dd9da44a0a9d5ee211e36c6a779a682469e106
Author: Benjamin Otte <otte redhat com>
Date:   Fri Feb 12 07:59:06 2016 +0100

    css: Add support for sums to calc()
    
    This requires adding code to do math on number values:
      gtk_css_number_value_multiply()
    and
      gtk_css_number_value_try_add()
    were added to achieve that.
    
    Some tests are included.

 gtk/gtkcsscalcvalue.c            |  282 +++++++++++++++++++++++++++++++++-----
 gtk/gtkcsscalcvalueprivate.h     |    3 +
 gtk/gtkcssdimensionvalue.c       |   21 +++-
 gtk/gtkcssnumbervalue.c          |   36 +++++
 gtk/gtkcssnumbervalueprivate.h   |   10 ++
 testsuite/css/parser/Makefile.am |    1 +
 testsuite/css/parser/calc.css    |    7 +
 7 files changed, 324 insertions(+), 36 deletions(-)
---
diff --git a/gtk/gtkcsscalcvalue.c b/gtk/gtkcsscalcvalue.c
index 51839c0..374b233 100644
--- a/gtk/gtkcsscalcvalue.c
+++ b/gtk/gtkcsscalcvalue.c
@@ -19,48 +19,127 @@
 
 #include "gtkcsscalcvalueprivate.h"
 
-#include "gtkcssenumvalueprivate.h"
-#include "gtkcssinitialvalueprivate.h"
-#include "gtkstylepropertyprivate.h"
-
-#include "fallback-c89.c"
+#include <string.h>
 
 struct _GtkCssValue {
   GTK_CSS_VALUE_BASE
-  GtkCssValue *term;
+  gsize                 n_terms;
+  GtkCssValue *         terms[1];
 };
 
+static gsize
+gtk_css_value_calc_get_size (gsize n_terms)
+{
+  g_assert (n_terms > 0);
+
+  return sizeof (GtkCssValue) + sizeof (GtkCssValue *) * (n_terms - 1);
+}
 static void
 gtk_css_value_calc_free (GtkCssValue *value)
 {
-  _gtk_css_value_unref (value->term);
-  g_slice_free (GtkCssValue, value);
+  gsize i;
+
+  for (i = 0; i < value->n_terms; i++)
+    {
+      _gtk_css_value_unref (value->terms[i]);
+    }
+
+  g_slice_free1 (gtk_css_value_calc_get_size (value->n_terms), value);
 }
 
-static GtkCssValue *gtk_css_calc_value_new (GtkCssValue *term);
+static GtkCssValue *gtk_css_calc_value_new (gsize n_terms);
+
+static GtkCssValue *
+gtk_css_value_new_from_array (GPtrArray *array)
+{
+  GtkCssValue *result;
+  
+  if (array->len > 1)
+    {
+      result = gtk_css_calc_value_new (array->len);
+      memcpy (result->terms, array->pdata, array->len * sizeof (GtkCssValue *));
+    }
+  else
+    {
+      result = g_ptr_array_index (array, 0);
+    }
+
+  g_ptr_array_free (array, TRUE);
+
+  return result;
+}
+
+static void
+gtk_css_calc_array_add (GPtrArray *array, GtkCssValue *value)
+{
+  gsize i;
+
+  for (i = 0; i < array->len; i++)
+    {
+      GtkCssValue *sum = gtk_css_number_value_try_add (g_ptr_array_index (array, i), value);
+
+      if (sum)
+        {
+          g_ptr_array_index (array, i) = sum;
+          _gtk_css_value_unref (value);
+          return;
+        }
+    }
+
+  g_ptr_array_add (array, value);
+}
 
 static GtkCssValue *
-gtk_css_value_calc_compute (GtkCssValue             *calc,
+gtk_css_value_calc_compute (GtkCssValue             *value,
                             guint                    property_id,
                             GtkStyleProviderPrivate *provider,
                             GtkCssStyle             *style,
                             GtkCssStyle             *parent_style)
 {
-  GtkCssValue *computed_term;
+  GtkCssValue *result;
+  GPtrArray *array;
+  gboolean changed;
+  gsize i;
 
-  computed_term = _gtk_css_value_compute (calc->term, property_id, provider, style, parent_style);
-  if (computed_term == calc->term)
-    return _gtk_css_value_ref (calc);
+  array = g_ptr_array_new ();
+  for (i = 0; i < value->n_terms; i++)
+    {
+      GtkCssValue *computed = _gtk_css_value_compute (value->terms[i], property_id, provider, style, 
parent_style);
+      changed |= computed != value->terms[i];
+      gtk_css_calc_array_add (array, computed);
+    }
+
+  if (changed)
+    {
+      result = gtk_css_value_new_from_array (array);
+    }
+  else
+    {
+      g_ptr_array_set_free_func (array, (GDestroyNotify) _gtk_css_value_unref);
+      g_ptr_array_free (array, TRUE);
+      result = _gtk_css_value_ref (value);
+    }
 
-  return gtk_css_calc_value_new (computed_term);
+  return result;
 }
 
 
 static gboolean
-gtk_css_value_calc_equal (const GtkCssValue *calc1,
-                          const GtkCssValue *calc2)
+gtk_css_value_calc_equal (const GtkCssValue *value1,
+                          const GtkCssValue *value2)
 {
-  return _gtk_css_value_equal (calc1->term, calc2->term);
+  gsize i;
+
+  if (value1->n_terms != value2->n_terms)
+    return FALSE;
+
+  for (i = 0; i < value1->n_terms; i++)
+    {
+      if (!_gtk_css_value_equal (value1->terms[i], value2->terms[i]))
+        return FALSE;
+    }
+
+  return TRUE;
 }
 
 static GtkCssValue *
@@ -73,31 +152,87 @@ gtk_css_value_calc_transition (GtkCssValue *start,
 }
 
 static void
-gtk_css_value_calc_print (const GtkCssValue *calc,
+gtk_css_value_calc_print (const GtkCssValue *value,
                           GString           *string)
 {
+  gsize i;
+
   g_string_append (string, "calc(");
-  _gtk_css_value_print (calc->term, string);
+  _gtk_css_value_print (value->terms[0], string);
+
+  for (i = 1; i < value->n_terms; i++)
+    {
+      g_string_append (string, " + ");
+      _gtk_css_value_print (value->terms[i], string);
+    }
   g_string_append (string, ")");
 }
 
-double
+static double
 gtk_css_value_calc_get (const GtkCssValue *value,
                         double             one_hundred_percent)
 {
-  return _gtk_css_number_value_get (value->term, one_hundred_percent);
+  double result = 0.0;
+  gsize i;
+
+  for (i = 0; i < value->n_terms; i++)
+    {
+      result += _gtk_css_number_value_get (value->terms[i], one_hundred_percent);
+    }
+
+  return result;
 }
 
-GtkCssDimension
+static GtkCssDimension
 gtk_css_value_calc_get_dimension (const GtkCssValue *value)
 {
-  return gtk_css_number_value_get_dimension (value->term);
+  GtkCssDimension dimension = GTK_CSS_DIMENSION_PERCENTAGE;
+  gsize i;
+
+  for (i = 0; i < value->n_terms && dimension == GTK_CSS_DIMENSION_PERCENTAGE; i++)
+    {
+      dimension = gtk_css_number_value_get_dimension (value->terms[i]);
+    }
+
+  return dimension;
 }
 
-gboolean
+static gboolean
 gtk_css_value_calc_has_percent (const GtkCssValue *value)
 {
-  return gtk_css_number_value_has_percent (value->term);
+  gsize i;
+
+  for (i = 0; i < value->n_terms; i++)
+    {
+      if (gtk_css_number_value_has_percent (value->terms[i]))
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+static GtkCssValue *
+gtk_css_value_calc_multiply (const GtkCssValue *value,
+                             double             factor)
+{
+  GtkCssValue *result;
+  gsize i;
+
+  result = gtk_css_calc_value_new (value->n_terms);
+
+  for (i = 0; i < value->n_terms; i++)
+    {
+      result->terms[i] = gtk_css_number_value_multiply (value->terms[i], factor);
+    }
+
+  return result;
+}
+
+static GtkCssValue *
+gtk_css_value_calc_try_add (const GtkCssValue *value1,
+                            const GtkCssValue *value2)
+{
+  return NULL;
 }
 
 static const GtkCssNumberValueClass GTK_CSS_VALUE_CALC = {
@@ -111,15 +246,92 @@ static const GtkCssNumberValueClass GTK_CSS_VALUE_CALC = {
   gtk_css_value_calc_get,
   gtk_css_value_calc_get_dimension,
   gtk_css_value_calc_has_percent,
+  gtk_css_value_calc_multiply,
+  gtk_css_value_calc_try_add
 };
 
 static GtkCssValue *
-gtk_css_calc_value_new (GtkCssValue *term)
+gtk_css_calc_value_new (gsize n_terms)
 {
   GtkCssValue *result;
 
-  result = _gtk_css_value_new (GtkCssValue, &GTK_CSS_VALUE_CALC.value_class);
-  result->term = term;
+  result = _gtk_css_value_alloc (&GTK_CSS_VALUE_CALC.value_class,
+                                 gtk_css_value_calc_get_size (n_terms));
+  result->n_terms = n_terms;
+
+  return result;
+}
+
+GtkCssValue *
+gtk_css_calc_value_new_sum (GtkCssValue *value1,
+                            GtkCssValue *value2)
+{
+  GPtrArray *array;
+  gsize i;
+
+  array = g_ptr_array_new ();
+
+  if (value1->class == &GTK_CSS_VALUE_CALC.value_class)
+    {
+      for (i = 0; i < value1->n_terms; i++)
+        {
+          gtk_css_calc_array_add (array, _gtk_css_value_ref (value1->terms[i]));
+        }
+    }
+  else
+    {
+      gtk_css_calc_array_add (array, _gtk_css_value_ref (value1));
+    }
+
+  if (value2->class == &GTK_CSS_VALUE_CALC.value_class)
+    {
+      for (i = 0; i < value2->n_terms; i++)
+        {
+          gtk_css_calc_array_add (array, _gtk_css_value_ref (value2->terms[i]));
+        }
+    }
+  else
+    {
+      gtk_css_calc_array_add (array, _gtk_css_value_ref (value2));
+    }
+
+  return gtk_css_value_new_from_array (array);
+}
+
+GtkCssValue *
+gtk_css_calc_value_parse_sum (GtkCssParser           *parser,
+                              GtkCssNumberParseFlags  flags)
+{
+  GtkCssValue *result;
+
+  result = _gtk_css_number_value_parse (parser, flags);
+  if (result == NULL)
+    return NULL;
+
+  while (_gtk_css_parser_begins_with (parser, '+') || _gtk_css_parser_begins_with (parser, '-'))
+    {
+      GtkCssValue *next, *temp;
+
+      if (_gtk_css_parser_try (parser, "+", TRUE))
+        {
+          next = _gtk_css_number_value_parse (parser, flags);
+        }
+      else if (_gtk_css_parser_try (parser, "-", TRUE))
+        {
+          temp = _gtk_css_number_value_parse (parser, flags);
+          next = gtk_css_number_value_multiply (temp, -1);
+          _gtk_css_value_unref (temp);
+        }
+      else
+        {
+          g_assert_not_reached ();
+        }
+
+      temp = gtk_css_number_value_add (result, next);
+      _gtk_css_value_unref (result);
+      _gtk_css_value_unref (next);
+      result = temp;
+    }
 
   return result;
 }
@@ -128,7 +340,7 @@ GtkCssValue *
 gtk_css_calc_value_parse (GtkCssParser           *parser,
                           GtkCssNumberParseFlags  flags)
 {
-  GtkCssValue *term;
+  GtkCssValue *value;
 
   if (!_gtk_css_parser_try (parser, "calc(", TRUE))
     {
@@ -136,17 +348,17 @@ gtk_css_calc_value_parse (GtkCssParser           *parser,
       return NULL;
     }
 
-  term = _gtk_css_number_value_parse (parser, flags);
-  if (term == NULL)
+  value = gtk_css_calc_value_parse_sum (parser, flags);
+  if (value == NULL)
     return NULL;
 
   if (!_gtk_css_parser_try (parser, ")", TRUE))
     {
-      _gtk_css_value_unref (term);
-      _gtk_css_parser_error (parser, "Expected ')' for calc() statement");
+      _gtk_css_value_unref (value);
+      _gtk_css_parser_error (parser, "Expected ')' after calc() statement");
       return NULL;
     }
 
-  return gtk_css_calc_value_new (term);
+  return value;
 }
 
diff --git a/gtk/gtkcsscalcvalueprivate.h b/gtk/gtkcsscalcvalueprivate.h
index 247d4c8..2d11315 100644
--- a/gtk/gtkcsscalcvalueprivate.h
+++ b/gtk/gtkcsscalcvalueprivate.h
@@ -22,6 +22,9 @@
 
 G_BEGIN_DECLS
 
+GtkCssValue *   gtk_css_calc_value_new_sum          (GtkCssValue            *value1,
+                                                     GtkCssValue            *value2);
+
 GtkCssValue *   gtk_css_calc_value_parse            (GtkCssParser           *parser,
                                                      GtkCssNumberParseFlags  flags);
 
diff --git a/gtk/gtkcssdimensionvalue.c b/gtk/gtkcssdimensionvalue.c
index edf8a6f..f1a4d90 100644
--- a/gtk/gtkcssdimensionvalue.c
+++ b/gtk/gtkcssdimensionvalue.c
@@ -241,6 +241,23 @@ gtk_css_value_dimension_has_percent (const GtkCssValue *value)
   return gtk_css_unit_get_dimension (value->unit) == GTK_CSS_DIMENSION_PERCENTAGE;
 }
 
+static GtkCssValue *
+gtk_css_value_dimension_multiply (const GtkCssValue *value,
+                                  double             factor)
+{
+  return gtk_css_dimension_value_new (value->value * factor, value->unit);
+}
+
+static GtkCssValue *
+gtk_css_value_dimension_try_add (const GtkCssValue *value1,
+                                 const GtkCssValue *value2)
+{
+  if (value1->unit != value2->unit)
+    return NULL;
+
+  return gtk_css_dimension_value_new (value1->value + value2->value, value1->unit);
+}
+
 static const GtkCssNumberValueClass GTK_CSS_VALUE_DIMENSION = {
   {
     gtk_css_value_dimension_free,
@@ -251,7 +268,9 @@ static const GtkCssNumberValueClass GTK_CSS_VALUE_DIMENSION = {
   },
   gtk_css_value_dimension_get,
   gtk_css_value_dimension_get_dimension,
-  gtk_css_value_dimension_has_percent
+  gtk_css_value_dimension_has_percent,
+  gtk_css_value_dimension_multiply,
+  gtk_css_value_dimension_try_add
 };
 
 GtkCssValue *
diff --git a/gtk/gtkcssnumbervalue.c b/gtk/gtkcssnumbervalue.c
index 12508db..b7892c7 100644
--- a/gtk/gtkcssnumbervalue.c
+++ b/gtk/gtkcssnumbervalue.c
@@ -43,6 +43,42 @@ gtk_css_number_value_has_percent (const GtkCssValue *value)
 }
 
 GtkCssValue *
+gtk_css_number_value_multiply (const GtkCssValue *value,
+                               double             factor)
+{
+  GtkCssNumberValueClass *number_value_class = (GtkCssNumberValueClass *) value->class;
+
+  return number_value_class->multiply (value, factor);
+}
+
+GtkCssValue *
+gtk_css_number_value_add (GtkCssValue *value1,
+                          GtkCssValue *value2)
+{
+  GtkCssValue *sum;
+
+  sum = gtk_css_number_value_try_add (value1, value2);
+  if (sum == NULL)
+    sum = gtk_css_calc_value_new_sum (value1, value2);
+
+  return sum;
+}
+
+GtkCssValue *
+gtk_css_number_value_try_add (const GtkCssValue *value1,
+                              const GtkCssValue *value2)
+{
+  GtkCssNumberValueClass *number_value_class;
+  
+  if (value1->class != value2->class)
+    return NULL;
+
+  number_value_class = (GtkCssNumberValueClass *) value1->class;
+
+  return number_value_class->try_add (value1, value2);
+}
+
+GtkCssValue *
 _gtk_css_number_value_new (double     value,
                            GtkCssUnit unit)
 {
diff --git a/gtk/gtkcssnumbervalueprivate.h b/gtk/gtkcssnumbervalueprivate.h
index e45eeaa..8e77aaa 100644
--- a/gtk/gtkcssnumbervalueprivate.h
+++ b/gtk/gtkcssnumbervalueprivate.h
@@ -45,6 +45,10 @@ struct _GtkCssNumberValueClass {
                                                      double                  one_hundred_percent);
   GtkCssDimension       (* get_dimension)           (const GtkCssValue      *value);
   gboolean              (* has_percent)             (const GtkCssValue      *value);
+  GtkCssValue *         (* multiply)                (const GtkCssValue      *value,
+                                                     double                  factor);
+  GtkCssValue *         (* try_add)                 (const GtkCssValue      *value1,
+                                                     const GtkCssValue      *value2);
 };
 
 GtkCssValue *   _gtk_css_number_value_new           (double                  value,
@@ -55,6 +59,12 @@ GtkCssValue *   _gtk_css_number_value_parse         (GtkCssParser           *par
 
 GtkCssDimension gtk_css_number_value_get_dimension  (const GtkCssValue      *value);
 gboolean        gtk_css_number_value_has_percent    (const GtkCssValue      *value);
+GtkCssValue *   gtk_css_number_value_multiply       (const GtkCssValue      *value,
+                                                     double                  factor);
+GtkCssValue *   gtk_css_number_value_add            (GtkCssValue            *value1,
+                                                     GtkCssValue            *value2);
+GtkCssValue *   gtk_css_number_value_try_add        (const GtkCssValue      *value1,
+                                                     const GtkCssValue      *value2);
 
 double          _gtk_css_number_value_get           (const GtkCssValue      *number,
                                                      double                  one_hundred_percent);
diff --git a/testsuite/css/parser/Makefile.am b/testsuite/css/parser/Makefile.am
index 28fa71f..0cc0cce 100644
--- a/testsuite/css/parser/Makefile.am
+++ b/testsuite/css/parser/Makefile.am
@@ -226,6 +226,7 @@ test_data = \
         border-width.ref.css \
         box-shadow.css \
         box-shadow.ref.css \
+       calc.css \
        calc-simple.css \
        calc-simple.ref.css \
        close-at-end-of-file.css \
diff --git a/testsuite/css/parser/calc.css b/testsuite/css/parser/calc.css
new file mode 100644
index 0000000..2c22095
--- /dev/null
+++ b/testsuite/css/parser/calc.css
@@ -0,0 +1,7 @@
+a {
+  margin-left: calc(3px + 1em);
+}
+
+a {
+  transition-duration: calc(1s - 100ms + -100ms);
+}


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