[gtk/wip/baedert/for-master: 64/75] Make dimension values and calc values just number values



commit cc51e1c0a43ea084e30dad638fdd2420d95d53a3
Author: Timm Bäder <mail baedert org>
Date:   Wed May 6 09:56:52 2020 +0200

    Make dimension values and calc values just number values

 gtk/gtkcsscalcvalue.c             | 294 ----------------
 gtk/gtkcsscalcvalueprivate.h      |   3 -
 gtk/gtkcssdimensionvalue.c        | 376 --------------------
 gtk/gtkcssdimensionvalueprivate.h |   6 -
 gtk/gtkcssimagecrossfade.c        |   2 +-
 gtk/gtkcssnumbervalue.c           | 701 +++++++++++++++++++++++++++++++++++---
 gtk/gtkcssnumbervalueprivate.h    |  24 +-
 gtk/gtkcssvalue.c                 |   5 -
 8 files changed, 649 insertions(+), 762 deletions(-)
---
diff --git a/gtk/gtkcsscalcvalue.c b/gtk/gtkcsscalcvalue.c
index 330f3c0724..ad7380b990 100644
--- a/gtk/gtkcsscalcvalue.c
+++ b/gtk/gtkcsscalcvalue.c
@@ -21,300 +21,6 @@
 
 #include <string.h>
 
-struct _GtkCssValue {
-  GTK_CSS_VALUE_BASE
-  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)
-{
-  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 (gsize n_terms);
-
-static GtkCssValue *
-gtk_css_value_new_from_array (GtkCssValue **values,
-                              guint         n_values)
-{
-  GtkCssValue *result;
-
-  if (n_values > 1)
-    {
-      result = gtk_css_calc_value_new (n_values);
-      memcpy (result->terms, values, n_values * sizeof (GtkCssValue *));
-    }
-  else
-    {
-      result = values[0];
-    }
-
-  return result;
-}
-
-static void
-gtk_css_calc_array_add (GPtrArray *array, GtkCssValue *value)
-{
-  gsize i;
-  gint calc_term_order;
-
-  calc_term_order = gtk_css_number_value_get_calc_term_order (value);
-
-  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;
-        }
-      else if (gtk_css_number_value_get_calc_term_order (g_ptr_array_index (array, i)) > calc_term_order)
-        {
-          g_ptr_array_insert (array, i, value);
-          return;
-        }
-    }
-
-  g_ptr_array_add (array, value);
-}
-
-static GtkCssValue *
-gtk_css_value_calc_compute (GtkCssValue      *value,
-                            guint             property_id,
-                            GtkStyleProvider *provider,
-                            GtkCssStyle      *style,
-                            GtkCssStyle      *parent_style)
-{
-  GtkCssValue *result;
-  gboolean changed = FALSE;
-  gsize i;
-  GtkCssValue **new_values;
-
-  new_values = g_alloca (sizeof (GtkCssValue *) * value->n_terms);
-
-  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];
-      new_values[i] = computed;
-    }
-
-  if (changed)
-    {
-      result = gtk_css_value_new_from_array (new_values, value->n_terms);
-    }
-  else
-    {
-      for (i = 0; i < value->n_terms; i++)
-        gtk_css_value_unref (new_values[i]);
-
-      result = _gtk_css_value_ref (value);
-    }
-
-  return result;
-}
-
-
-static gboolean
-gtk_css_value_calc_equal (const GtkCssValue *value1,
-                          const GtkCssValue *value2)
-{
-  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 void
-gtk_css_value_calc_print (const GtkCssValue *value,
-                          GString           *string)
-{
-  gsize i;
-
-  g_string_append (string, "calc(");
-  _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, ")");
-}
-
-static double
-gtk_css_value_calc_get (const GtkCssValue *value,
-                        double             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;
-}
-
-static GtkCssDimension
-gtk_css_value_calc_get_dimension (const GtkCssValue *value)
-{
-  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;
-}
-
-static gboolean
-gtk_css_value_calc_has_percent (const GtkCssValue *value)
-{
-  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 (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 (GtkCssValue *value1,
-                            GtkCssValue *value2)
-{
-  return NULL;
-}
-
-static gint
-gtk_css_value_calc_get_calc_term_order (const GtkCssValue *value)
-{
-  /* This should never be needed because calc() can't contain calc(),
-   * but eh...
-   */
-  return 0;
-}
-
-static const GtkCssNumberValueClass GTK_CSS_VALUE_CALC = {
-  {
-    "GtkCssCalcValue",
-    gtk_css_value_calc_free,
-    gtk_css_value_calc_compute,
-    gtk_css_value_calc_equal,
-    gtk_css_number_value_transition,
-    NULL,
-    NULL,
-    gtk_css_value_calc_print
-  },
-  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,
-  gtk_css_value_calc_get_calc_term_order
-};
-
-static GtkCssValue *
-gtk_css_calc_value_new (gsize n_terms)
-{
-  GtkCssValue *result;
-
-  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;
-  GtkCssValue *result;
-  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));
-    }
-
-  result = gtk_css_value_new_from_array ((GtkCssValue **)array->pdata, array->len);
-  g_ptr_array_free (array, TRUE);
-
-  return result;
-}
-
 GtkCssValue *   gtk_css_calc_value_parse_sum (GtkCssParser           *parser,
                                               GtkCssNumberParseFlags  flags);
 
diff --git a/gtk/gtkcsscalcvalueprivate.h b/gtk/gtkcsscalcvalueprivate.h
index 2d113150ff..247d4c8a2a 100644
--- a/gtk/gtkcsscalcvalueprivate.h
+++ b/gtk/gtkcsscalcvalueprivate.h
@@ -22,9 +22,6 @@
 
 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 10effe22d7..385661b029 100644
--- a/gtk/gtkcssdimensionvalue.c
+++ b/gtk/gtkcssdimensionvalue.c
@@ -19,370 +19,6 @@
 
 #include "gtkcssdimensionvalueprivate.h"
 
-#include "gtkcssenumvalueprivate.h"
-#include "gtkstylepropertyprivate.h"
-
-#include "fallback-c89.c"
-
-struct _GtkCssValue {
-  GTK_CSS_VALUE_BASE
-  GtkCssUnit unit;
-  double value;
-};
-
-static void
-gtk_css_value_dimension_free (GtkCssValue *value)
-{
-  g_slice_free (GtkCssValue, value);
-}
-
-static double
-get_base_font_size_px (guint             property_id,
-                       GtkStyleProvider *provider,
-                       GtkCssStyle      *style,
-                       GtkCssStyle      *parent_style)
-{
-  if (property_id == GTK_CSS_PROPERTY_FONT_SIZE)
-    {
-      if (parent_style)
-        return _gtk_css_number_value_get (parent_style->core->font_size, 100);
-      else
-        return gtk_css_font_size_get_default_px (provider, style);
-    }
-
-  return _gtk_css_number_value_get (style->core->font_size, 100);
-}
-
-static double
-get_dpi (GtkCssStyle *style)
-{
-  return _gtk_css_number_value_get (style->core->dpi, 96);
-}
-
-static GtkCssValue *
-gtk_css_value_dimension_compute (GtkCssValue      *number,
-                                 guint             property_id,
-                                 GtkStyleProvider *provider,
-                                 GtkCssStyle      *style,
-                                 GtkCssStyle      *parent_style)
-{
-  switch (number->unit)
-    {
-    case GTK_CSS_PERCENT:
-      /* percentages for font sizes are computed, other percentages aren't */
-      if (property_id == GTK_CSS_PROPERTY_FONT_SIZE)
-        return gtk_css_dimension_value_new (number->value / 100.0 * 
-                                            get_base_font_size_px (property_id, provider, style, 
parent_style),
-                                            GTK_CSS_PX);
-      G_GNUC_FALLTHROUGH;
-    case GTK_CSS_NUMBER:
-    case GTK_CSS_PX:
-    case GTK_CSS_DEG:
-    case GTK_CSS_S:
-      return _gtk_css_value_ref (number);
-    case GTK_CSS_PT:
-      return gtk_css_dimension_value_new (number->value * get_dpi (style) / 72.0,
-                                          GTK_CSS_PX);
-    case GTK_CSS_PC:
-      return gtk_css_dimension_value_new (number->value * get_dpi (style) / 72.0 * 12.0,
-                                          GTK_CSS_PX);
-    case GTK_CSS_IN:
-      return gtk_css_dimension_value_new (number->value * get_dpi (style),
-                                          GTK_CSS_PX);
-    case GTK_CSS_CM:
-      return gtk_css_dimension_value_new (number->value * get_dpi (style) * 0.39370078740157477,
-                                          GTK_CSS_PX);
-    case GTK_CSS_MM:
-      return gtk_css_dimension_value_new (number->value * get_dpi (style) * 0.039370078740157477,
-                                          GTK_CSS_PX);
-    case GTK_CSS_EM:
-      return gtk_css_dimension_value_new (number->value *
-                                          get_base_font_size_px (property_id, provider, style, parent_style),
-                                          GTK_CSS_PX);
-    case GTK_CSS_EX:
-      /* for now we pretend ex is half of em */
-      return gtk_css_dimension_value_new (number->value * 0.5 *
-                                          get_base_font_size_px (property_id, provider, style, parent_style),
-                                          GTK_CSS_PX);
-    case GTK_CSS_REM:
-      return gtk_css_dimension_value_new (number->value *
-                                          gtk_css_font_size_get_default_px (provider, style),
-                                          GTK_CSS_PX);
-    case GTK_CSS_RAD:
-      return gtk_css_dimension_value_new (number->value * 360.0 / (2 * G_PI),
-                                          GTK_CSS_DEG);
-    case GTK_CSS_GRAD:
-      return gtk_css_dimension_value_new (number->value * 360.0 / 400.0,
-                                          GTK_CSS_DEG);
-    case GTK_CSS_TURN:
-      return gtk_css_dimension_value_new (number->value * 360.0,
-                                          GTK_CSS_DEG);
-    case GTK_CSS_MS:
-      return gtk_css_dimension_value_new (number->value / 1000.0,
-                                          GTK_CSS_S);
-    default:
-      g_assert_not_reached();
-    }
-}
-
-static gboolean
-gtk_css_value_dimension_equal (const GtkCssValue *number1,
-                            const GtkCssValue *number2)
-{
-  return number1->unit == number2->unit &&
-         number1->value == number2->value;
-}
-
-static void
-gtk_css_value_dimension_print (const GtkCssValue *number,
-                            GString           *string)
-{
-  char buf[G_ASCII_DTOSTR_BUF_SIZE];
-
-  const char *names[] = {
-    /* [GTK_CSS_NUMBER] = */ "",
-    /* [GTK_CSS_PERCENT] = */ "%",
-    /* [GTK_CSS_PX] = */ "px",
-    /* [GTK_CSS_PT] = */ "pt",
-    /* [GTK_CSS_EM] = */ "em",
-    /* [GTK_CSS_EX] = */ "ex",
-    /* [GTK_CSS_REM] = */ "rem",
-    /* [GTK_CSS_PC] = */ "pc",
-    /* [GTK_CSS_IN] = */ "in",
-    /* [GTK_CSS_CM] = */ "cm",
-    /* [GTK_CSS_MM] = */ "mm",
-    /* [GTK_CSS_RAD] = */ "rad",
-    /* [GTK_CSS_DEG] = */ "deg",
-    /* [GTK_CSS_GRAD] = */ "grad",
-    /* [GTK_CSS_TURN] = */ "turn",
-    /* [GTK_CSS_S] = */ "s",
-    /* [GTK_CSS_MS] = */ "ms",
-  };
-
-  if (isinf (number->value))
-    g_string_append (string, "infinite");
-  else
-    {
-      g_ascii_dtostr (buf, sizeof (buf), number->value);
-      g_string_append (string, buf);
-      if (number->value != 0.0)
-        g_string_append (string, names[number->unit]);
-    }
-}
-
-static double
-gtk_css_value_dimension_get (const GtkCssValue *value,
-                             double             one_hundred_percent)
-{
-  if (value->unit == GTK_CSS_PERCENT)
-    return value->value * one_hundred_percent / 100;
-  else
-    return value->value;
-}
-
-static GtkCssDimension
-gtk_css_value_dimension_get_dimension (const GtkCssValue *value)
-{
-  return gtk_css_unit_get_dimension (value->unit);
-}
-
-static gboolean
-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 (GtkCssValue *value,
-                                  double       factor)
-{
-  return gtk_css_dimension_value_new (value->value * factor, value->unit);
-}
-
-static GtkCssValue *
-gtk_css_value_dimension_try_add (GtkCssValue *value1,
-                                 GtkCssValue *value2)
-{
-  if (value1->unit != value2->unit)
-    return NULL;
-
-  if (value1->value == 0)
-    return _gtk_css_value_ref (value2);
-
-  if (value2->value == 0)
-    return _gtk_css_value_ref (value1);
-
-  return gtk_css_dimension_value_new (value1->value + value2->value, value1->unit);
-}
-
-static gint
-gtk_css_value_dimension_get_calc_term_order (const GtkCssValue *value)
-{
-  /* note: the order is alphabetic */
-  guint order_per_unit[] = {
-    /* [GTK_CSS_NUMBER] = */ 0,
-    /* [GTK_CSS_PERCENT] = */ 16,
-    /* [GTK_CSS_PX] = */ 11,
-    /* [GTK_CSS_PT] = */ 10,
-    /* [GTK_CSS_EM] = */ 3,
-    /* [GTK_CSS_EX] = */ 4,
-    /* [GTK_CSS_REM] = */ 13,
-    /* [GTK_CSS_PC] = */ 9,
-    /* [GTK_CSS_IN] = */ 6,
-    /* [GTK_CSS_CM] = */ 1,
-    /* [GTK_CSS_MM] = */ 7,
-    /* [GTK_CSS_RAD] = */ 12,
-    /* [GTK_CSS_DEG] = */ 2,
-    /* [GTK_CSS_GRAD] = */ 5,
-    /* [GTK_CSS_TURN] = */ 15,
-    /* [GTK_CSS_S] = */ 14,
-    /* [GTK_CSS_MS] = */ 8
-  };
-
-  return 1000 + order_per_unit[value->unit];
-}
-
-static GtkCssValue *
-gtk_css_value_dimension_transition (GtkCssValue *start,
-                                    GtkCssValue *end,
-                                    guint        property_id,
-                                    double       progress)
-{
-  if (start->unit == end->unit)
-    return gtk_css_dimension_value_new (start->value + (end->value - start->value) * progress, start->unit);
-
-  return gtk_css_number_value_transition (start, end, property_id, progress);
-}
-
-static const GtkCssNumberValueClass GTK_CSS_VALUE_DIMENSION = {
-  {
-    "GtkCssDimensionValue",
-    gtk_css_value_dimension_free,
-    gtk_css_value_dimension_compute,
-    gtk_css_value_dimension_equal,
-    gtk_css_value_dimension_transition,
-    NULL,
-    NULL,
-    gtk_css_value_dimension_print
-  },
-  gtk_css_value_dimension_get,
-  gtk_css_value_dimension_get_dimension,
-  gtk_css_value_dimension_has_percent,
-  gtk_css_value_dimension_multiply,
-  gtk_css_value_dimension_try_add,
-  gtk_css_value_dimension_get_calc_term_order
-};
-
-GtkCssValue *
-gtk_css_dimension_value_new (double     value,
-                             GtkCssUnit unit)
-{
-  static GtkCssValue number_singletons[] = {
-    { &GTK_CSS_VALUE_DIMENSION.value_class, 1, TRUE, GTK_CSS_NUMBER, 0 },
-    { &GTK_CSS_VALUE_DIMENSION.value_class, 1, TRUE, GTK_CSS_NUMBER, 1 },
-    { &GTK_CSS_VALUE_DIMENSION.value_class, 1, TRUE, GTK_CSS_NUMBER, 96 }, /* DPI default */
-  };
-  static GtkCssValue px_singletons[] = {
-    { &GTK_CSS_VALUE_DIMENSION.value_class, 1, TRUE, GTK_CSS_PX, 0 },
-    { &GTK_CSS_VALUE_DIMENSION.value_class, 1, TRUE, GTK_CSS_PX, 1 },
-    { &GTK_CSS_VALUE_DIMENSION.value_class, 1, TRUE, GTK_CSS_PX, 2 },
-    { &GTK_CSS_VALUE_DIMENSION.value_class, 1, TRUE, GTK_CSS_PX, 3 },
-    { &GTK_CSS_VALUE_DIMENSION.value_class, 1, TRUE, GTK_CSS_PX, 4 },
-    { &GTK_CSS_VALUE_DIMENSION.value_class, 1, TRUE, GTK_CSS_PX, 8 },
-    { &GTK_CSS_VALUE_DIMENSION.value_class, 1, TRUE, GTK_CSS_PX, 16 }, /* Icon size default */
-    { &GTK_CSS_VALUE_DIMENSION.value_class, 1, TRUE, GTK_CSS_PX, 32 },
-    { &GTK_CSS_VALUE_DIMENSION.value_class, 1, TRUE, GTK_CSS_PX, 64 },
-  };
-  static GtkCssValue percent_singletons[] = {
-    { &GTK_CSS_VALUE_DIMENSION.value_class, 1, TRUE, GTK_CSS_PERCENT, 0 },
-    { &GTK_CSS_VALUE_DIMENSION.value_class, 1, FALSE, GTK_CSS_PERCENT, 50 },
-    { &GTK_CSS_VALUE_DIMENSION.value_class, 1, FALSE, GTK_CSS_PERCENT, 100 },
-  };
-  static GtkCssValue second_singletons[] = {
-    { &GTK_CSS_VALUE_DIMENSION.value_class, 1, TRUE, GTK_CSS_S, 0 },
-    { &GTK_CSS_VALUE_DIMENSION.value_class, 1, TRUE, GTK_CSS_S, 1 },
-  };
-  static GtkCssValue deg_singletons[] = {
-    { &GTK_CSS_VALUE_DIMENSION.value_class, 1, TRUE, GTK_CSS_DEG, 0   },
-    { &GTK_CSS_VALUE_DIMENSION.value_class, 1, TRUE, GTK_CSS_DEG, 90  },
-    { &GTK_CSS_VALUE_DIMENSION.value_class, 1, TRUE, GTK_CSS_DEG, 180 },
-    { &GTK_CSS_VALUE_DIMENSION.value_class, 1, TRUE, GTK_CSS_DEG, 270 },
-  };
-  GtkCssValue *result;
-
-  switch ((guint)unit)
-    {
-    case GTK_CSS_NUMBER:
-      if (value == 0 || value == 1)
-        return _gtk_css_value_ref (&number_singletons[(int) value]);
-
-      if (value == 96)
-        return _gtk_css_value_ref (&number_singletons[2]);
-
-      break;
-
-    case GTK_CSS_PX:
-      if (value == 0 ||
-          value == 1 ||
-          value == 2 ||
-          value == 3 ||
-          value == 4)
-        return _gtk_css_value_ref (&px_singletons[(int) value]);
-      if (value == 8)
-        return _gtk_css_value_ref (&px_singletons[5]);
-      if (value == 16)
-        return _gtk_css_value_ref (&px_singletons[6]);
-      if (value == 32)
-        return _gtk_css_value_ref (&px_singletons[7]);
-      if (value == 64)
-        return _gtk_css_value_ref (&px_singletons[8]);
-
-      break;
-
-    case GTK_CSS_PERCENT:
-      if (value == 0)
-        return _gtk_css_value_ref (&percent_singletons[0]);
-      if (value == 50)
-        return _gtk_css_value_ref (&percent_singletons[1]);
-      if (value == 100)
-        return _gtk_css_value_ref (&percent_singletons[2]);
-
-      break;
-
-    case GTK_CSS_S:
-      if (value == 0 || value == 1)
-        return _gtk_css_value_ref (&second_singletons[(int)value]);
-
-      break;
-
-    case GTK_CSS_DEG:
-      if (value == 0)
-        return _gtk_css_value_ref (&deg_singletons[0]);
-      if (value == 90)
-        return _gtk_css_value_ref (&deg_singletons[1]);
-      if (value == 180)
-        return _gtk_css_value_ref (&deg_singletons[2]);
-      if (value == 270)
-        return _gtk_css_value_ref (&deg_singletons[3]);
-
-      break;
-
-    default:
-      ;
-    }
-
-  result = _gtk_css_value_new (GtkCssValue, &GTK_CSS_VALUE_DIMENSION.value_class);
-  result->unit = unit;
-  result->value = value;
-  result->is_computed = value == 0 ||
-                        unit == GTK_CSS_NUMBER ||
-                        unit == GTK_CSS_PX ||
-                        unit == GTK_CSS_DEG ||
-                        unit == GTK_CSS_S;
-
-  return result;
-}
-
 GtkCssValue *
 gtk_css_dimension_value_parse (GtkCssParser           *parser,
                                GtkCssNumberParseFlags  flags)
@@ -495,15 +131,3 @@ gtk_css_dimension_value_parse (GtkCssParser           *parser,
 
   return result;
 }
-
-gboolean
-gtk_css_dimension_value_is_zero (const GtkCssValue *value)
-{
-  if (!value)
-    return TRUE;
-
-  if (value->class != &GTK_CSS_VALUE_DIMENSION.value_class)
-    return FALSE;
-
-  return value->value == 0;
-}
diff --git a/gtk/gtkcssdimensionvalueprivate.h b/gtk/gtkcssdimensionvalueprivate.h
index fea2a919ef..71370d8768 100644
--- a/gtk/gtkcssdimensionvalueprivate.h
+++ b/gtk/gtkcssdimensionvalueprivate.h
@@ -24,14 +24,8 @@
 
 G_BEGIN_DECLS
 
-GtkCssValue *   gtk_css_dimension_value_new         (double                  value,
-                                                     GtkCssUnit              unit);
-
 GtkCssValue *   gtk_css_dimension_value_parse       (GtkCssParser           *parser,
                                                      GtkCssNumberParseFlags  flags);
-
-gboolean        gtk_css_dimension_value_is_zero     (const GtkCssValue      *value) G_GNUC_PURE;
-
 G_END_DECLS
 
 #endif /* __GTK_CSS_DIMENSION_VALUE_PRIVATE_H__ */
diff --git a/gtk/gtkcssimagecrossfade.c b/gtk/gtkcssimagecrossfade.c
index 2d281b26cc..4ec2c3f0f1 100644
--- a/gtk/gtkcssimagecrossfade.c
+++ b/gtk/gtkcssimagecrossfade.c
@@ -294,7 +294,7 @@ parse_progress (GtkCssParser *parser,
 
   if (*progress > 1.0)
     {
-      gtk_css_parser_error_value (parser, "Percentages over 100%% are not allowed");
+      gtk_css_parser_error_value (parser, "Percentages over 100%% are not allowed. Given value: %f", 
*progress);
       return FALSE;
     }
 
diff --git a/gtk/gtkcssnumbervalue.c b/gtk/gtkcssnumbervalue.c
index 625559580e..d63563783f 100644
--- a/gtk/gtkcssnumbervalue.c
+++ b/gtk/gtkcssnumbervalue.c
@@ -20,66 +20,449 @@
 #include "gtkcssnumbervalueprivate.h"
 
 #include "gtkcsscalcvalueprivate.h"
+#include "gtkcssenumvalueprivate.h"
 #include "gtkcssdimensionvalueprivate.h"
+#include "gtkcssstyleprivate.h"
 #include "gtkprivate.h"
 
+static GtkCssValue *        gtk_css_calc_value_new         (guint n_terms);
+static GtkCssValue *        gtk_css_calc_value_new_sum     (GtkCssValue *a,
+                                                            GtkCssValue *b);
+
+enum {
+  TYPE_CALC = 0,
+  TYPE_DIMENSION = 1,
+};
+
 struct _GtkCssValue {
   GTK_CSS_VALUE_BASE
+  guint type : 1; /* Calc or dimension */
+  union {
+    struct {
+      GtkCssUnit unit;
+      double value;
+    } dimension;
+    struct {
+      guint n_terms;
+      GtkCssValue *terms[1];
+    } calc;
+  };
 };
 
-GtkCssDimension
-gtk_css_number_value_get_dimension (const GtkCssValue *value)
+
+static GtkCssValue *
+gtk_css_calc_value_new_from_array (GtkCssValue **values,
+                                   guint         n_values)
 {
-  GtkCssNumberValueClass *number_value_class = (GtkCssNumberValueClass *) value->class;
+  GtkCssValue *result;
+
+  if (n_values > 1)
+    {
+      result = gtk_css_calc_value_new (n_values);
+      memcpy (result->calc.terms, values, n_values * sizeof (GtkCssValue *));
+    }
+  else
+    {
+      result = values[0];
+    }
 
-  return number_value_class->get_dimension (value);
+  return result;
 }
 
-gboolean
-gtk_css_number_value_has_percent (const GtkCssValue *value)
+
+static void
+gtk_css_value_number_free (GtkCssValue *value)
 {
-  GtkCssNumberValueClass *number_value_class = (GtkCssNumberValueClass *) value->class;
+  g_slice_free (GtkCssValue, value);
+}
 
-  return number_value_class->has_percent (value);
+static double
+get_dpi (GtkCssStyle *style)
+{
+  return _gtk_css_number_value_get (style->core->dpi, 96);
 }
 
-GtkCssValue *
-gtk_css_number_value_multiply (GtkCssValue *value,
-                               double       factor)
+static double
+get_base_font_size_px (guint             property_id,
+                       GtkStyleProvider *provider,
+                       GtkCssStyle      *style,
+                       GtkCssStyle      *parent_style)
 {
-  GtkCssNumberValueClass *number_value_class = (GtkCssNumberValueClass *) value->class;
+  if (property_id == GTK_CSS_PROPERTY_FONT_SIZE)
+    {
+      if (parent_style)
+        return _gtk_css_number_value_get (parent_style->core->font_size, 100);
+      else
+        return gtk_css_font_size_get_default_px (provider, style);
+    }
+
+  return _gtk_css_number_value_get (style->core->font_size, 100);
+}
 
-  if (factor == 1)
-    return _gtk_css_value_ref (value);
+static GtkCssValue *
+gtk_css_value_number_compute (GtkCssValue      *number,
+                              guint             property_id,
+                              GtkStyleProvider *provider,
+                              GtkCssStyle      *style,
+                              GtkCssStyle      *parent_style)
+{
+  double value;
+
+  if (G_UNLIKELY (number->type == TYPE_CALC))
+    {
+      const guint n_terms = number->calc.n_terms;
+      GtkCssValue *result;
+      gboolean changed = FALSE;
+      gsize i;
+      GtkCssValue **new_values;
+
+      new_values = g_alloca (sizeof (GtkCssValue *) * n_terms);
+
+      for (i = 0; i < n_terms; i++)
+        {
+          GtkCssValue *computed = _gtk_css_value_compute (number->calc.terms[i],
+                                                          property_id, provider, style,
+                                                          parent_style);
+          changed |= computed != number->calc.terms[i];
+          new_values[i] = computed;
+        }
+
+      if (changed)
+        {
+          result = gtk_css_calc_value_new_from_array (new_values, n_terms);
+        }
+      else
+        {
+          for (i = 0; i < n_terms; i++)
+            gtk_css_value_unref (new_values[i]);
+
+          result = _gtk_css_value_ref (number);
+        }
+
+      return result;
+    }
+
+  g_assert (number->type == TYPE_DIMENSION);
+
+  value = number->dimension.value;
+  switch (number->dimension.unit)
+    {
+    case GTK_CSS_PERCENT:
+      /* percentages for font sizes are computed, other percentages aren't */
+      if (property_id == GTK_CSS_PROPERTY_FONT_SIZE)
+        return gtk_css_dimension_value_new (value / 100.0 *
+                                            get_base_font_size_px (property_id, provider, style, 
parent_style),
+                                            GTK_CSS_PX);
+      G_GNUC_FALLTHROUGH;
+    case GTK_CSS_NUMBER:
+    case GTK_CSS_PX:
+    case GTK_CSS_DEG:
+    case GTK_CSS_S:
+      return _gtk_css_value_ref (number);
+    case GTK_CSS_PT:
+      return gtk_css_dimension_value_new (value * get_dpi (style) / 72.0,
+                                          GTK_CSS_PX);
+    case GTK_CSS_PC:
+      return gtk_css_dimension_value_new (value * get_dpi (style) / 72.0 * 12.0,
+                                          GTK_CSS_PX);
+    case GTK_CSS_IN:
+      return gtk_css_dimension_value_new (value * get_dpi (style),
+                                          GTK_CSS_PX);
+    case GTK_CSS_CM:
+      return gtk_css_dimension_value_new (value * get_dpi (style) * 0.39370078740157477,
+                                          GTK_CSS_PX);
+    case GTK_CSS_MM:
+      return gtk_css_dimension_value_new (value * get_dpi (style) * 0.039370078740157477,
+                                          GTK_CSS_PX);
+    case GTK_CSS_EM:
+      return gtk_css_dimension_value_new (value *
+                                          get_base_font_size_px (property_id, provider, style, parent_style),
+                                          GTK_CSS_PX);
+    case GTK_CSS_EX:
+      /* for now we pretend ex is half of em */
+      return gtk_css_dimension_value_new (value * 0.5 *
+                                          get_base_font_size_px (property_id, provider, style, parent_style),
+                                          GTK_CSS_PX);
+    case GTK_CSS_REM:
+      return gtk_css_dimension_value_new (value *
+                                          gtk_css_font_size_get_default_px (provider, style),
+                                          GTK_CSS_PX);
+    case GTK_CSS_RAD:
+      return gtk_css_dimension_value_new (value * 360.0 / (2 * G_PI),
+                                          GTK_CSS_DEG);
+    case GTK_CSS_GRAD:
+      return gtk_css_dimension_value_new (value * 360.0 / 400.0,
+                                          GTK_CSS_DEG);
+    case GTK_CSS_TURN:
+      return gtk_css_dimension_value_new (value * 360.0,
+                                          GTK_CSS_DEG);
+    case GTK_CSS_MS:
+      return gtk_css_dimension_value_new (value / 1000.0,
+                                          GTK_CSS_S);
+    default:
+      g_assert_not_reached();
+    }
+}
+
+static gboolean
+gtk_css_value_number_equal (const GtkCssValue *val1,
+                            const GtkCssValue *val2)
+{
+  guint i;
+
+  if (val1->type != val2->type)
+    return FALSE;
+
+  if (G_LIKELY (val1->type == TYPE_DIMENSION))
+    {
+      return val1->dimension.unit == val2->dimension.unit &&
+             val1->dimension.value == val2->dimension.value;
+    }
 
-  return number_value_class->multiply (value, factor);
+  g_assert (val1->type == TYPE_CALC);
+
+  if (val1->calc.n_terms != val2->calc.n_terms)
+    return FALSE;
+
+  for (i = 0; i < val1->calc.n_terms; i++)
+    {
+      if (!_gtk_css_value_equal (val1->calc.terms[i], val2->calc.terms[i]))
+        return FALSE;
+    }
+
+  return TRUE;
 }
 
-GtkCssValue *
-gtk_css_number_value_add (GtkCssValue *value1,
-                          GtkCssValue *value2)
+static void
+gtk_css_value_number_print (const GtkCssValue *value,
+                            GString           *string)
 {
-  GtkCssValue *sum;
+  guint i;
+
+  if (G_LIKELY (value->type == TYPE_DIMENSION))
+    {
+      const char *names[] = {
+        /* [GTK_CSS_NUMBER] = */ "",
+        /* [GTK_CSS_PERCENT] = */ "%",
+        /* [GTK_CSS_PX] = */ "px",
+        /* [GTK_CSS_PT] = */ "pt",
+        /* [GTK_CSS_EM] = */ "em",
+        /* [GTK_CSS_EX] = */ "ex",
+        /* [GTK_CSS_REM] = */ "rem",
+        /* [GTK_CSS_PC] = */ "pc",
+        /* [GTK_CSS_IN] = */ "in",
+        /* [GTK_CSS_CM] = */ "cm",
+        /* [GTK_CSS_MM] = */ "mm",
+        /* [GTK_CSS_RAD] = */ "rad",
+        /* [GTK_CSS_DEG] = */ "deg",
+        /* [GTK_CSS_GRAD] = */ "grad",
+        /* [GTK_CSS_TURN] = */ "turn",
+        /* [GTK_CSS_S] = */ "s",
+        /* [GTK_CSS_MS] = */ "ms",
+      };
+      char buf[G_ASCII_DTOSTR_BUF_SIZE];
+
+      if (isinf (value->dimension.value))
+        g_string_append (string, "infinite");
+      else
+        {
+          g_ascii_dtostr (buf, sizeof (buf), value->dimension.value);
+          g_string_append (string, buf);
+          if (value->dimension.value != 0.0)
+            g_string_append (string, names[value->dimension.unit]);
+        }
+
+      return;
+    }
+
+  g_assert (value->type == TYPE_CALC);
+
+  g_string_append (string, "calc(");
+  _gtk_css_value_print (value->calc.terms[0], string);
+
+  for (i = 1; i < value->calc.n_terms; i++)
+    {
+      g_string_append (string, " + ");
+      _gtk_css_value_print (value->calc.terms[i], string);
+    }
+  g_string_append (string, ")");
+}
 
-  sum = gtk_css_number_value_try_add (value1, value2);
-  if (sum == NULL)
-    sum = gtk_css_calc_value_new_sum (value1, value2);
+static GtkCssValue *
+gtk_css_value_number_transition (GtkCssValue *start,
+                                 GtkCssValue *end,
+                                 guint        property_id,
+                                 double       progress)
+{
+  GtkCssValue *result, *mul_start, *mul_end;
 
-  return sum;
+  if (start == end)
+    return _gtk_css_value_ref (start);
+
+  if (G_LIKELY (start->type == TYPE_DIMENSION && end->type == TYPE_DIMENSION))
+    {
+      if (start->dimension.unit == end->dimension.unit)
+        {
+          const double start_value = start->dimension.value;
+          const double end_value = end->dimension.value;
+          return gtk_css_dimension_value_new (start_value + (end_value - start_value) * progress,
+                                              start->dimension.unit);
+        }
+    }
+
+  mul_start = gtk_css_number_value_multiply (start, 1 - progress);
+  mul_end = gtk_css_number_value_multiply (end, progress);
+
+  result = gtk_css_number_value_add (mul_start, mul_end);
+
+  _gtk_css_value_unref (mul_start);
+  _gtk_css_value_unref (mul_end);
+
+  return result;
 }
 
-GtkCssValue *
-gtk_css_number_value_try_add (GtkCssValue *value1,
-                              GtkCssValue *value2)
+static const GtkCssValueClass GTK_CSS_VALUE_NUMBER = {
+  "GtkCssNumberValue",
+  gtk_css_value_number_free,
+  gtk_css_value_number_compute,
+  gtk_css_value_number_equal,
+  gtk_css_value_number_transition,
+  NULL,
+  NULL,
+  gtk_css_value_number_print
+};
+
+static gsize
+gtk_css_value_calc_get_size (gsize n_terms)
 {
-  GtkCssNumberValueClass *number_value_class;
-  
-  if (value1->class != value2->class)
-    return NULL;
+  g_assert (n_terms > 0);
+
+  return sizeof (GtkCssValue) + sizeof (GtkCssValue *) * (n_terms - 1);
+}
 
-  number_value_class = (GtkCssNumberValueClass *) value1->class;
+static GtkCssValue *
+gtk_css_calc_value_new (guint n_terms)
+{
+  GtkCssValue *result;
+
+  result = _gtk_css_value_alloc (&GTK_CSS_VALUE_NUMBER,
+                                 gtk_css_value_calc_get_size (n_terms));
+  result->type = TYPE_CALC;
+  result->calc.n_terms = n_terms;
+
+  return result;
+}
 
-  return number_value_class->try_add (value1, value2);
+GtkCssValue *
+gtk_css_dimension_value_new (double     value,
+                             GtkCssUnit unit)
+{
+  static GtkCssValue number_singletons[] = {
+    { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {{ GTK_CSS_NUMBER, 0 }} },
+    { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {{ GTK_CSS_NUMBER, 1 }} },
+    { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {{ GTK_CSS_NUMBER, 96 }} }, /* DPI default */
+  };
+  static GtkCssValue px_singletons[] = {
+    { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {{ GTK_CSS_PX, 0 }} },
+    { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {{ GTK_CSS_PX, 1 }} },
+    { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {{ GTK_CSS_PX, 2 }} },
+    { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {{ GTK_CSS_PX, 3 }} },
+    { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {{ GTK_CSS_PX, 4 }} },
+    { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {{ GTK_CSS_PX, 8 }} },
+    { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {{ GTK_CSS_PX, 16 }} }, /* Icon size default */
+    { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {{ GTK_CSS_PX, 32 }} },
+    { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {{ GTK_CSS_PX, 64 }} },
+  };
+  static GtkCssValue percent_singletons[] = {
+    { &GTK_CSS_VALUE_NUMBER, 1, TRUE,  TYPE_DIMENSION, {{ GTK_CSS_PERCENT, 0 }} },
+    { &GTK_CSS_VALUE_NUMBER, 1, FALSE, TYPE_DIMENSION, {{ GTK_CSS_PERCENT, 50 }} },
+    { &GTK_CSS_VALUE_NUMBER, 1, FALSE, TYPE_DIMENSION, {{ GTK_CSS_PERCENT, 100 }} },
+  };
+  static GtkCssValue second_singletons[] = {
+    { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {{ GTK_CSS_S, 0 }} },
+    { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {{ GTK_CSS_S, 1 }} },
+  };
+  static GtkCssValue deg_singletons[] = {
+    { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {{ GTK_CSS_DEG, 0 }} },
+    { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {{ GTK_CSS_DEG, 90 }} },
+    { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {{ GTK_CSS_DEG, 180 }} },
+    { &GTK_CSS_VALUE_NUMBER, 1, TRUE, TYPE_DIMENSION, {{ GTK_CSS_DEG, 270 }} },
+  };
+  GtkCssValue *result;
+
+  switch ((guint)unit)
+    {
+    case GTK_CSS_NUMBER:
+      if (value == 0 || value == 1)
+        return _gtk_css_value_ref (&number_singletons[(int) value]);
+
+      if (value == 96)
+        return _gtk_css_value_ref (&number_singletons[2]);
+
+      break;
+
+    case GTK_CSS_PX:
+      if (value == 0 ||
+          value == 1 ||
+          value == 2 ||
+          value == 3 ||
+          value == 4)
+        return _gtk_css_value_ref (&px_singletons[(int) value]);
+      if (value == 8)
+        return _gtk_css_value_ref (&px_singletons[5]);
+      if (value == 16)
+        return _gtk_css_value_ref (&px_singletons[6]);
+      if (value == 32)
+        return _gtk_css_value_ref (&px_singletons[7]);
+      if (value == 64)
+        return _gtk_css_value_ref (&px_singletons[8]);
+
+      break;
+
+    case GTK_CSS_PERCENT:
+      if (value == 0)
+        return _gtk_css_value_ref (&percent_singletons[0]);
+      if (value == 50)
+        return _gtk_css_value_ref (&percent_singletons[1]);
+      if (value == 100)
+        return _gtk_css_value_ref (&percent_singletons[2]);
+
+      break;
+
+    case GTK_CSS_S:
+      if (value == 0 || value == 1)
+        return _gtk_css_value_ref (&second_singletons[(int)value]);
+
+      break;
+
+    case GTK_CSS_DEG:
+      if (value == 0)
+        return _gtk_css_value_ref (&deg_singletons[0]);
+      if (value == 90)
+        return _gtk_css_value_ref (&deg_singletons[1]);
+      if (value == 180)
+        return _gtk_css_value_ref (&deg_singletons[2]);
+      if (value == 270)
+        return _gtk_css_value_ref (&deg_singletons[3]);
+
+      break;
+
+    default:
+      ;
+    }
+
+  result = _gtk_css_value_new (GtkCssValue, &GTK_CSS_VALUE_NUMBER);
+  result->type = TYPE_DIMENSION;
+  result->dimension.unit = unit;
+  result->dimension.value = value;
+  result->is_computed = value == 0 ||
+                        unit == GTK_CSS_NUMBER ||
+                        unit == GTK_CSS_PX ||
+                        unit == GTK_CSS_DEG ||
+                        unit == GTK_CSS_S;
+  if (value == 5040)
+  g_critical ("%s: %f", __FUNCTION__, value);
+  return result;
 }
 
 /*
@@ -95,43 +478,224 @@ gtk_css_number_value_try_add (GtkCssValue *value1,
  * Returns: Magic value determining placement when printing calc()
  *     expression.
  */
-gint
+static int
 gtk_css_number_value_get_calc_term_order (const GtkCssValue *value)
 {
-  GtkCssNumberValueClass *number_value_class = (GtkCssNumberValueClass *) value->class;
+  if (G_LIKELY (value->type == TYPE_DIMENSION))
+    {
+      /* note: the order is alphabetic */
+      static const guint order_per_unit[] = {
+        /* [GTK_CSS_NUMBER] = */ 0,
+        /* [GTK_CSS_PERCENT] = */ 16,
+        /* [GTK_CSS_PX] = */ 11,
+        /* [GTK_CSS_PT] = */ 10,
+        /* [GTK_CSS_EM] = */ 3,
+        /* [GTK_CSS_EX] = */ 4,
+        /* [GTK_CSS_REM] = */ 13,
+        /* [GTK_CSS_PC] = */ 9,
+        /* [GTK_CSS_IN] = */ 6,
+        /* [GTK_CSS_CM] = */ 1,
+        /* [GTK_CSS_MM] = */ 7,
+        /* [GTK_CSS_RAD] = */ 12,
+        /* [GTK_CSS_DEG] = */ 2,
+        /* [GTK_CSS_GRAD] = */ 5,
+        /* [GTK_CSS_TURN] = */ 15,
+        /* [GTK_CSS_S] = */ 14,
+        /* [GTK_CSS_MS] = */ 8
+      };
+      return 1000 + order_per_unit[value->dimension.unit];
+    }
+
+  g_assert (value->type == TYPE_CALC);
+
+  /* This should never be needed because calc() can't contain calc(),
+   * but eh...
+   */
+  return 0;
+}
 
-  return number_value_class->get_calc_term_order (value);
+static void
+gtk_css_calc_array_add (GPtrArray *array, GtkCssValue *value)
+{
+  gsize i;
+  gint calc_term_order;
+
+  calc_term_order = gtk_css_number_value_get_calc_term_order (value);
+
+  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;
+        }
+      else if (gtk_css_number_value_get_calc_term_order (g_ptr_array_index (array, i)) > calc_term_order)
+        {
+          g_ptr_array_insert (array, i, value);
+          return;
+        }
+    }
+
+  g_ptr_array_add (array, value);
 }
 
-GtkCssValue *
-_gtk_css_number_value_new (double     value,
-                           GtkCssUnit unit)
+static GtkCssValue *
+gtk_css_calc_value_new_sum (GtkCssValue *value1,
+                            GtkCssValue *value2)
 {
-  return gtk_css_dimension_value_new (value, unit);
+  GPtrArray *array;
+  GtkCssValue *result;
+  gsize i;
+
+  array = g_ptr_array_new ();
+
+  if (value1->class == &GTK_CSS_VALUE_NUMBER &&
+      value1->type == TYPE_CALC)
+    {
+      for (i = 0; i < value1->calc.n_terms; i++)
+        {
+          gtk_css_calc_array_add (array, _gtk_css_value_ref (value1->calc.terms[i]));
+        }
+    }
+  else
+    {
+      gtk_css_calc_array_add (array, _gtk_css_value_ref (value1));
+    }
+
+  if (value2->class == &GTK_CSS_VALUE_NUMBER &&
+      value2->type == TYPE_CALC)
+    {
+      for (i = 0; i < value2->calc.n_terms; i++)
+        {
+          gtk_css_calc_array_add (array, _gtk_css_value_ref (value2->calc.terms[i]));
+        }
+    }
+  else
+    {
+      gtk_css_calc_array_add (array, _gtk_css_value_ref (value2));
+    }
+
+  result = gtk_css_calc_value_new_from_array ((GtkCssValue **)array->pdata, array->len);
+  g_ptr_array_free (array, TRUE);
+
+  return result;
+}
+
+GtkCssDimension
+gtk_css_number_value_get_dimension (const GtkCssValue *value)
+{
+  GtkCssDimension dimension = GTK_CSS_DIMENSION_PERCENTAGE;
+  guint i;
+
+  if (G_LIKELY (value->type == TYPE_DIMENSION))
+    return gtk_css_unit_get_dimension (value->dimension.unit);
+
+  g_assert (value->type == TYPE_CALC);
+
+  for (i = 0; i < value->calc.n_terms && dimension == GTK_CSS_DIMENSION_PERCENTAGE; i++)
+    {
+      dimension = gtk_css_number_value_get_dimension (value->calc.terms[i]);
+      if (dimension != GTK_CSS_DIMENSION_PERCENTAGE)
+        break;
+    }
+
+  return dimension;
+}
+
+gboolean
+gtk_css_number_value_has_percent (const GtkCssValue *value)
+{
+  guint i;
+
+  if (G_LIKELY (value->type == TYPE_DIMENSION))
+    {
+      return gtk_css_unit_get_dimension (value->dimension.unit) == GTK_CSS_DIMENSION_PERCENTAGE;
+    }
+
+  for (i = 0; i < value->calc.n_terms; i++)
+    {
+      if (gtk_css_number_value_has_percent (value->calc.terms[i]))
+        return TRUE;
+    }
+
+  return FALSE;
 }
 
 GtkCssValue *
-gtk_css_number_value_transition (GtkCssValue *start,
-                                 GtkCssValue *end,
-                                 guint        property_id,
-                                 double       progress)
+gtk_css_number_value_multiply (GtkCssValue *value,
+                               double       factor)
 {
-  GtkCssValue *result, *mul_start, *mul_end;
+  GtkCssValue *result;
+  guint i;
 
-  if (start == end)
-    return _gtk_css_value_ref (start);
+  if (factor == 1)
+    return _gtk_css_value_ref (value);
 
-  mul_start = gtk_css_number_value_multiply (start, 1 - progress);
-  mul_end = gtk_css_number_value_multiply (end, progress);
+  if (G_LIKELY (value->type == TYPE_DIMENSION))
+    {
+      return gtk_css_dimension_value_new (value->dimension.value * factor,
+                                          value->dimension.unit);
+    }
 
-  result = gtk_css_number_value_add (mul_start, mul_end);
+  g_assert (value->type == TYPE_CALC);
 
-  _gtk_css_value_unref (mul_start);
-  _gtk_css_value_unref (mul_end);
+  result = gtk_css_calc_value_new (value->calc.n_terms);
+  for (i = 0; i < value->calc.n_terms; i++)
+    result->calc.terms[i] = gtk_css_number_value_multiply (value->calc.terms[i], factor);
 
   return result;
 }
 
+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 (GtkCssValue *value1,
+                              GtkCssValue *value2)
+{
+  if (G_UNLIKELY (value1->type != value2->type))
+    return NULL;
+
+  if (G_LIKELY (value1->type == TYPE_DIMENSION))
+    {
+      if (value1->dimension.unit != value2->dimension.unit)
+        return NULL;
+
+      if (value1->dimension.value == 0)
+        return _gtk_css_value_ref (value2);
+
+      if (value2->dimension.value == 0)
+        return _gtk_css_value_ref (value1);
+
+      return gtk_css_dimension_value_new (value1->dimension.value + value2->dimension.value,
+                                          value1->dimension.unit);
+    }
+
+  g_assert (value1->type == TYPE_CALC);
+  /* Not possible for calc() values */
+  return NULL;
+}
+
+GtkCssValue *
+_gtk_css_number_value_new (double     value,
+                           GtkCssUnit unit)
+{
+  return gtk_css_dimension_value_new (value, unit);
+}
+
 gboolean
 gtk_css_number_value_can_parse (GtkCssParser *parser)
 {
@@ -157,15 +721,40 @@ _gtk_css_number_value_parse (GtkCssParser           *parser,
 }
 
 double
-_gtk_css_number_value_get (const GtkCssValue *number,
+_gtk_css_number_value_get (const GtkCssValue *value,
                            double             one_hundred_percent)
 {
-  GtkCssNumberValueClass *number_value_class;
+  double result;
+  guint i;
+
+  if (G_LIKELY (value->type == TYPE_DIMENSION))
+    {
+      if (value->dimension.unit == GTK_CSS_PERCENT)
+        return value->dimension.value * one_hundred_percent / 100;
+      else
+        return value->dimension.value;
+    }
 
-  gtk_internal_return_val_if_fail (number != NULL, 0.0);
+  g_assert (value->type == TYPE_CALC);
 
-  number_value_class = (GtkCssNumberValueClass *) number->class;
+  result = 0.0;
+  for (i = 0; i < value->calc.n_terms; i++)
+    result += _gtk_css_number_value_get (value->calc.terms[i], one_hundred_percent);
 
-  return number_value_class->get (number, one_hundred_percent);
+  return result;
 }
 
+gboolean
+gtk_css_dimension_value_is_zero (const GtkCssValue *value)
+{
+  if (!value)
+    return TRUE;
+
+  if (value->class != &GTK_CSS_VALUE_NUMBER)
+    return FALSE;
+
+  if (value->type != TYPE_DIMENSION)
+    return FALSE;
+
+  return value->dimension.value == 0;
+}
diff --git a/gtk/gtkcssnumbervalueprivate.h b/gtk/gtkcssnumbervalueprivate.h
index 040ba902da..bcac90e45e 100644
--- a/gtk/gtkcssnumbervalueprivate.h
+++ b/gtk/gtkcssnumbervalueprivate.h
@@ -35,28 +35,11 @@ typedef enum /*< skip >*/ {
   GTK_CSS_PARSE_TIME = (1 << 5)
 } GtkCssNumberParseFlags;
 
-typedef struct _GtkCssNumberValueClass GtkCssNumberValueClass;
-
-struct _GtkCssNumberValueClass {
-  GtkCssValueClass      value_class;
-
-  double                (* get)                     (const GtkCssValue      *value,
-                                                     double                  one_hundred_percent);
-  GtkCssDimension       (* get_dimension)           (const GtkCssValue      *value);
-  gboolean              (* has_percent)             (const GtkCssValue      *value);
-  GtkCssValue *         (* multiply)                (GtkCssValue            *value,
-                                                     double                  factor);
-  GtkCssValue *         (* try_add)                 (GtkCssValue            *value1,
-                                                     GtkCssValue            *value2);
-  gint                  (* get_calc_term_order)     (const GtkCssValue      *value);
-};
+GtkCssValue *   gtk_css_dimension_value_new         (double                  value,
+                                                     GtkCssUnit              unit);
 
 GtkCssValue *   _gtk_css_number_value_new           (double                  value,
                                                      GtkCssUnit              unit);
-GtkCssValue *   gtk_css_number_value_transition     (GtkCssValue            *start,
-                                                     GtkCssValue            *end,
-                                                     guint                   property_id,
-                                                     double                  progress);
 gboolean        gtk_css_number_value_can_parse      (GtkCssParser           *parser);
 GtkCssValue *   _gtk_css_number_value_parse         (GtkCssParser           *parser,
                                                      GtkCssNumberParseFlags  flags);
@@ -69,11 +52,10 @@ GtkCssValue *   gtk_css_number_value_add            (GtkCssValue            *val
                                                      GtkCssValue            *value2);
 GtkCssValue *   gtk_css_number_value_try_add        (GtkCssValue            *value1,
                                                      GtkCssValue            *value2);
-gint            gtk_css_number_value_get_calc_term_order (const GtkCssValue *value);
-
 double          _gtk_css_number_value_get           (const GtkCssValue      *number,
                                                      double                  one_hundred_percent) 
G_GNUC_PURE;
 
+gboolean        gtk_css_dimension_value_is_zero     (const GtkCssValue      *value) G_GNUC_PURE;
 
 G_END_DECLS
 
diff --git a/gtk/gtkcssvalue.c b/gtk/gtkcssvalue.c
index ce6c3fac3b..b48f82a3c7 100644
--- a/gtk/gtkcssvalue.c
+++ b/gtk/gtkcssvalue.c
@@ -263,11 +263,6 @@ _gtk_css_value_transition (GtkCssValue *start,
   gtk_internal_return_val_if_fail (start != NULL, FALSE);
   gtk_internal_return_val_if_fail (end != NULL, FALSE);
 
-  /* We compare functions here instead of classes so that number
-   * values can all transition to each other */
-  if (start->class->transition != end->class->transition)
-    return NULL;
-
   if (progress == 0)
     return _gtk_css_value_ref (start);
 


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