[gtk/wip/ebassi/constraint-layout: 95/105] Add GtkConstraintLayout



commit 9756cb9482b5cef1a831edfdae3b2e8bf7270a21
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Tue Apr 9 15:33:52 2019 +0100

    Add GtkConstraintLayout
    
    A layout manager using GtkConstraintSolver to measure and allocate
    children.

 gtk/gtk.h                        |    2 +
 gtk/gtkconstraint.c              |  583 ++++++++++++++++++++
 gtk/gtkconstraint.h              |   85 +++
 gtk/gtkconstraintlayout.c        | 1108 ++++++++++++++++++++++++++++++++++++++
 gtk/gtkconstraintlayout.h        |   53 ++
 gtk/gtkconstraintprivate.h       |   62 +++
 gtk/gtkconstraintsolver.c        |   31 --
 gtk/gtkconstraintsolverprivate.h |   31 ++
 gtk/gtkenums.h                   |   60 +++
 gtk/meson.build                  |    4 +
 10 files changed, 1988 insertions(+), 31 deletions(-)
---
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 6691d286c9..ecf4829339 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -82,6 +82,8 @@
 #include <gtk/gtkcolorutils.h>
 #include <gtk/gtkcombobox.h>
 #include <gtk/gtkcomboboxtext.h>
+#include <gtk/gtkconstraintlayout.h>
+#include <gtk/gtkconstraint.h>
 #include <gtk/gtkcontainer.h>
 #include <gtk/gtkcssprovider.h>
 #include <gtk/gtkcustomlayout.h>
diff --git a/gtk/gtkconstraint.c b/gtk/gtkconstraint.c
new file mode 100644
index 0000000000..d0c152b56d
--- /dev/null
+++ b/gtk/gtkconstraint.c
@@ -0,0 +1,583 @@
+/* gtkconstraint.c: Constraint between two widgets
+ * Copyright 2019  GNOME Foundation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Emmanuele Bassi
+ */
+
+/**
+ * SECTION:gtkconstraint
+ * @Title: GtkConstraint
+ * @Short_description: The description of a constraint
+ *
+ * #GtkConstraint describes a constraint between an attribute on a widget
+ * and another attribute on another widget, expressed as a linear equation
+ * like:
+ *
+ * |[
+ *   target.attr1 = source.attr2 × multiplier + constant
+ * ]|
+ *
+ * Each #GtkConstraint is part of a system that will be solved by a
+ * #GtkConstraintLayout in order to allocate and position each child widget.
+ *
+ * The source and target widgets, as well as their attributes, of a
+ * #GtkConstraint instance are immutable after creation.
+ */
+
+#include "config.h"
+
+#include "gtkconstraintprivate.h"
+#include "gtkconstraintsolverprivate.h"
+#include "gtkintl.h"
+#include "gtktypebuiltins.h"
+#include "gtkwidget.h"
+
+enum {
+  PROP_TARGET_WIDGET = 1,
+  PROP_TARGET_ATTRIBUTE,
+  PROP_RELATION,
+  PROP_SOURCE_WIDGET,
+  PROP_SOURCE_ATTRIBUTE,
+  PROP_MULTIPLIER,
+  PROP_CONSTANT,
+  PROP_STRENGTH,
+
+  N_PROPERTIES
+};
+
+static GParamSpec *obj_props[N_PROPERTIES];
+
+G_DEFINE_TYPE (GtkConstraint, gtk_constraint, G_TYPE_OBJECT)
+
+static void
+gtk_constraint_set_property (GObject      *gobject,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+  GtkConstraint *self = GTK_CONSTRAINT (gobject);
+
+  switch (prop_id)
+    {
+    case PROP_TARGET_WIDGET:
+      self->target_widget = g_value_get_object (value);
+      break;
+
+    case PROP_TARGET_ATTRIBUTE:
+      self->target_attribute = g_value_get_enum (value);
+      break;
+
+    case PROP_RELATION:
+      self->relation = g_value_get_enum (value);
+      break;
+
+    case PROP_SOURCE_WIDGET:
+      self->source_widget = g_value_get_object (value);
+      break;
+
+    case PROP_SOURCE_ATTRIBUTE:
+      self->source_attribute = g_value_get_enum (value);
+      break;
+
+    case PROP_MULTIPLIER:
+      self->multiplier = g_value_get_double (value);
+      break;
+
+    case PROP_CONSTANT:
+      self->constant = g_value_get_double (value);
+      break;
+
+    case PROP_STRENGTH:
+      self->strength = g_value_get_int (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gtk_constraint_get_property (GObject    *gobject,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+  GtkConstraint *self = GTK_CONSTRAINT (gobject);
+
+  switch (prop_id)
+    {
+    case PROP_TARGET_WIDGET:
+      g_value_set_object (value, self->target_widget);
+      break;
+
+    case PROP_TARGET_ATTRIBUTE:
+      g_value_set_enum (value, self->target_attribute);
+      break;
+
+    case PROP_RELATION:
+      g_value_set_enum (value, self->relation);
+      break;
+
+    case PROP_SOURCE_WIDGET:
+      g_value_set_object (value, self->source_widget);
+      break;
+
+    case PROP_SOURCE_ATTRIBUTE:
+      g_value_set_enum (value, self->source_attribute);
+      break;
+
+    case PROP_MULTIPLIER:
+      g_value_set_double (value, self->multiplier);
+      break;
+
+    case PROP_CONSTANT:
+      g_value_set_double (value, self->constant);
+      break;
+
+    case PROP_STRENGTH:
+      g_value_set_int (value, self->strength);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gtk_constraint_finalize (GObject *gobject)
+{
+  GtkConstraint *self = GTK_CONSTRAINT (gobject);
+
+  gtk_constraint_detach (self);
+
+  G_OBJECT_CLASS (gtk_constraint_parent_class)->finalize (gobject);
+}
+
+static void
+gtk_constraint_class_init (GtkConstraintClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->set_property = gtk_constraint_set_property;
+  gobject_class->get_property = gtk_constraint_get_property;
+  gobject_class->finalize = gtk_constraint_finalize;
+
+  /**
+   * GtkConstraint:target-widget:
+   *
+   * The target widget of the constraint.
+   *
+   * The constraint will set the #GtkConstraint:target-attribute of the
+   * target widget using the #GtkConstraint:source-attribute of the source
+   * widget.
+   */
+  obj_props[PROP_TARGET_WIDGET] =
+    g_param_spec_object ("target-widget",
+                         P_("Target Widget"),
+                         P_("The target widget of the constraint"),
+                         GTK_TYPE_WIDGET,
+                         G_PARAM_READWRITE |
+                         G_PARAM_STATIC_STRINGS |
+                         G_PARAM_CONSTRUCT_ONLY);
+  /**
+   * GtkConstraint:target-attribute:
+   *
+   * The attribute of the #GtkConstraint:target-widget set by the constraint.
+   */
+  obj_props[PROP_TARGET_ATTRIBUTE] =
+    g_param_spec_enum ("target-attribute",
+                       P_("Target Attribute"),
+                       P_("The attribute of the target widget set by the constraint"),
+                       GTK_TYPE_CONSTRAINT_ATTRIBUTE,
+                       GTK_CONSTRAINT_ATTRIBUTE_NONE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS |
+                       G_PARAM_CONSTRUCT_ONLY);
+  /**
+   * GtkConstraint:relation:
+   *
+   * The relation order between the terms of the constraint.
+   */
+  obj_props[PROP_RELATION] =
+    g_param_spec_enum ("relation",
+                       P_("Relation"),
+                       P_("The relation between the source and target attributes"),
+                       GTK_TYPE_CONSTRAINT_RELATION,
+                       GTK_CONSTRAINT_RELATION_EQ,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS |
+                       G_PARAM_CONSTRUCT_ONLY);
+  /**
+   * GtkConstraint:source-widget:
+   *
+   * The source widget of the constraint.
+   *
+   * The constraint will set the #GtkConstraint:target-attribute of the
+   * target widget using the #GtkConstraint:source-attribute of the source
+   * widget.
+   */
+  obj_props[PROP_SOURCE_WIDGET] =
+    g_param_spec_object ("source-widget",
+                         P_("Source Widget"),
+                         P_("The source widget of the constraint"),
+                         GTK_TYPE_WIDGET,
+                         G_PARAM_READWRITE |
+                         G_PARAM_STATIC_STRINGS |
+                         G_PARAM_CONSTRUCT_ONLY);
+  /**
+   * GtkConstraint:source-attribute:
+   *
+   * The attribute of the #GtkConstraint:source-widget read by the constraint.
+   */
+  obj_props[PROP_SOURCE_ATTRIBUTE] =
+    g_param_spec_enum ("source-attribute",
+                       P_("Source Attribute"),
+                       P_("The attribute of the source widget set by the constraint"),
+                       GTK_TYPE_CONSTRAINT_ATTRIBUTE,
+                       GTK_CONSTRAINT_ATTRIBUTE_NONE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS |
+                       G_PARAM_CONSTRUCT_ONLY);
+  /**
+   * GtkConstraint:multiplier:
+   *
+   * The multiplication factor to be applied to the
+   * #GtkConstraint:source-attribue.
+   */
+  obj_props[PROP_MULTIPLIER] =
+    g_param_spec_double ("multiplier",
+                         P_("Multiplier"),
+                         P_("The multiplication factor to be applied to the source attribute"),
+                         -G_MAXDOUBLE, G_MAXDOUBLE, 1.0,
+                         G_PARAM_READWRITE |
+                         G_PARAM_STATIC_STRINGS |
+                         G_PARAM_CONSTRUCT_ONLY);
+  /**
+   * GtkConstraint:constant:
+   *
+   * The constant value to be added to the #GtkConstraint:source-attribute.
+   */
+  obj_props[PROP_CONSTANT] =
+    g_param_spec_double ("constant",
+                         P_("Constant"),
+                         P_("The constant to be added to the source attribute"),
+                         -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+                         G_PARAM_READWRITE |
+                         G_PARAM_STATIC_STRINGS |
+                         G_PARAM_CONSTRUCT_ONLY);
+  /**
+   * GtkConstraint:strength:
+   *
+   * The strength of the constraint.
+   *
+   * The strength can be expressed either using one of the symbolic values
+   * of the #GtkConstraintStrength enumeration, or any positive integer
+   * value.
+   */
+  obj_props[PROP_STRENGTH] =
+    g_param_spec_int ("strength",
+                      P_("Strength"),
+                      P_("The strength of the constraint"),
+                      GTK_CONSTRAINT_STRENGTH_WEAK, G_MAXINT,
+                      GTK_CONSTRAINT_STRENGTH_REQUIRED,
+                      G_PARAM_READWRITE |
+                      G_PARAM_STATIC_STRINGS |
+                      G_PARAM_CONSTRUCT_ONLY);
+
+  g_object_class_install_properties (gobject_class, N_PROPERTIES, obj_props);
+}
+
+static void
+gtk_constraint_init (GtkConstraint *self)
+{
+  self->multiplier = 1.0;
+  self->constant = 0.0;
+
+  self->target_attribute = GTK_CONSTRAINT_ATTRIBUTE_NONE;
+  self->source_attribute = GTK_CONSTRAINT_ATTRIBUTE_NONE;
+  self->relation = GTK_CONSTRAINT_RELATION_EQ;
+  self->strength = GTK_CONSTRAINT_STRENGTH_REQUIRED;
+}
+
+/**
+ * gtk_constraint_new:
+ * @target_widget: (nullable): a #GtkWidget
+ * @target_attribute: the attribute of @target_widget to be set
+ * @relation: the relation equivalence between @target_attribute and @source_attribute
+ * @source_widget: (nullable): a #GtkWidget
+ * @source_attribute: the attribute of @source_widget to be read
+ * @multiplier: a multiplication factor to be applied to @source_attribute
+ * @constant: a constant factor to be added to @source_attribute
+ * @strength: the strength of the constraint
+ *
+ * Creates a new #GtkConstraint representing a relation between a layout
+ * attribute on a source #GtkWidget and a layout attribute on a target
+ * #GtkWidget.
+ *
+ * Returns: the newly created #GtkConstraint
+ */
+GtkConstraint *
+gtk_constraint_new (GtkWidget              *target_widget,
+                    GtkConstraintAttribute  target_attribute,
+                    GtkConstraintRelation   relation,
+                    GtkWidget              *source_widget,
+                    GtkConstraintAttribute  source_attribute,
+                    double                  multiplier,
+                    double                  constant,
+                    int                     strength)
+{
+  g_return_val_if_fail (target_widget == NULL || GTK_IS_WIDGET (target_widget), NULL);
+  g_return_val_if_fail (source_widget == NULL || GTK_IS_WIDGET (source_widget), NULL);
+
+  return g_object_new (GTK_TYPE_CONSTRAINT,
+                       "target-widget", target_widget,
+                       "target-attribute", target_attribute,
+                       "relation", relation,
+                       "source-widget", source_widget,
+                       "source-attribute", source_attribute,
+                       "multiplier", multiplier,
+                       "constant", constant,
+                       "strength", strength,
+                       NULL);
+}
+
+/**
+ * gtk_constraint_new_constant:
+ * @target_widget: (nullable): a #GtkWidget
+ * @target_attribute: the attribute of @target_widget to be set
+ * @relation: the relation equivalence between @target_attribute and @constant
+ * @constant: a constant factor to be set on @target_attribute
+ * @strength: the strength of the constraint
+ *
+ * Creates a new #GtkConstraint representing a relation between a layout
+ * attribute on a target #GtkWidget and a constant value.
+ *
+ * Returns: the newly created #GtkConstraint
+ */
+GtkConstraint *
+gtk_constraint_new_constant (GtkWidget              *target_widget,
+                             GtkConstraintAttribute  target_attribute,
+                             GtkConstraintRelation   relation,
+                             double                  constant,
+                             int                     strength)
+{
+  g_return_val_if_fail (target_widget == NULL || GTK_IS_WIDGET (target_widget), NULL);
+
+  return g_object_new (GTK_TYPE_CONSTRAINT,
+                       "target-widget", target_widget,
+                       "target-attribute", target_attribute,
+                       "relation", relation,
+                       "source-attribute", GTK_CONSTRAINT_ATTRIBUTE_NONE,
+                       "constant", constant,
+                       "strength", strength,
+                       NULL);
+}
+
+/**
+ * gtk_constraint_get_target_widget:
+ * @constraint: a #GtkConstraint
+ *
+ * Retrieves the target widget for the @constraint.
+ *
+ * Returns: (transfer none) (nullable): a #GtkWidget
+ */
+GtkWidget *
+gtk_constraint_get_target_widget (GtkConstraint *constraint)
+{
+  g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), NULL);
+
+  return constraint->target_widget;
+}
+
+GtkConstraintAttribute
+gtk_constraint_get_target_attribute (GtkConstraint *constraint)
+{
+  g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), GTK_CONSTRAINT_ATTRIBUTE_NONE);
+
+  return constraint->target_attribute;
+}
+
+/**
+ * gtk_constraint_get_source_widget:
+ * @constraint: a #GtkConstraint
+ *
+ * Retrieves the source widget for the @constraint.
+ *
+ * Returns: (transfer none) (nullable): a #GtkWidget
+ */
+GtkWidget *
+gtk_constraint_get_source_widget (GtkConstraint *constraint)
+{
+  g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), NULL);
+
+  return constraint->source_widget;
+}
+
+GtkConstraintAttribute
+gtk_constraint_get_source_attribute (GtkConstraint *constraint)
+{
+  g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), GTK_CONSTRAINT_ATTRIBUTE_NONE);
+
+  return constraint->source_attribute;
+}
+
+GtkConstraintRelation
+gtk_constraint_get_relation (GtkConstraint *constraint)
+{
+  g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), GTK_CONSTRAINT_RELATION_EQ);
+
+  return constraint->relation;
+}
+
+double
+gtk_constraint_get_multiplier (GtkConstraint *constraint)
+{
+  g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), 1.0);
+
+  return constraint->multiplier;
+}
+
+double
+gtk_constraint_get_constant (GtkConstraint *constraint)
+{
+  g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), 0.0);
+
+  return constraint->constant;
+}
+
+int
+gtk_constraint_get_strength (GtkConstraint *constraint)
+{
+  g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), GTK_CONSTRAINT_STRENGTH_REQUIRED);
+
+  return constraint->strength;
+}
+
+/*< private >
+ * gtk_constraint_get_weight:
+ * @constraint: a #GtkConstraint
+ *
+ * Computes the weight of the @constraint to be used with
+ * #GtkConstraintSolver.
+ *
+ * Returns: the weight of the constraint
+ */
+double
+gtk_constraint_get_weight (GtkConstraint *constraint)
+{
+  if (constraint->strength > 0)
+    return constraint->strength;
+
+  switch (constraint->strength)
+    {
+    case GTK_CONSTRAINT_STRENGTH_REQUIRED:
+      return GTK_CONSTRAINT_WEIGHT_REQUIRED;
+
+    case GTK_CONSTRAINT_STRENGTH_STRONG:
+      return GTK_CONSTRAINT_WEIGHT_STRONG;
+
+    case GTK_CONSTRAINT_STRENGTH_MEDIUM:
+      return GTK_CONSTRAINT_WEIGHT_MEDIUM;
+
+    case GTK_CONSTRAINT_STRENGTH_WEAK:
+      return GTK_CONSTRAINT_WEIGHT_WEAK;
+
+    default:
+      g_assert_not_reached ();
+    }
+
+  return 0;
+}
+
+/**
+ * gtk_constraint_is_required:
+ * @constraint: a #GtkConstraint
+ *
+ * Checks whether the @constraint is a required relation for solving the
+ * constraint layout.
+ *
+ * Returns: %TRUE if the constraint is required
+ */
+gboolean
+gtk_constraint_is_required (GtkConstraint *constraint)
+{
+  g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), FALSE);
+
+  return constraint->strength == GTK_CONSTRAINT_STRENGTH_REQUIRED;
+}
+
+/**
+ * gtk_constraint_is_attached:
+ * @constraint: a #GtkConstraint
+ *
+ * Checks whether the @constraint is attached to a #GtkConstraintLayout,
+ * and it is contributing to the layout.
+ *
+ * Returns: %TRUE if the constraint is attached
+ */
+gboolean
+gtk_constraint_is_attached (GtkConstraint *constraint)
+{
+  g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), FALSE);
+
+  return constraint->constraint_ref != NULL;
+}
+
+/**
+ * gtk_constraint_is_constant:
+ * @constraint: a #GtkConstraint
+ *
+ * Checks whether the @constraint describes a relation between an attribute
+ * on the #GtkConstraint:target-widget and a constant value.
+ *
+ * Returns: %TRUE if the constraint is a constant relation
+ */
+gboolean
+gtk_constraint_is_constant (GtkConstraint *constraint)
+{
+  g_return_val_if_fail (GTK_IS_CONSTRAINT (constraint), FALSE);
+
+  return constraint->source_widget == NULL &&
+         constraint->source_attribute == GTK_CONSTRAINT_ATTRIBUTE_NONE;
+}
+
+void
+gtk_constraint_attach (GtkConstraint       *constraint,
+                       GtkConstraintSolver *solver,
+                       GtkConstraintRef    *ref)
+{
+  g_return_if_fail (GTK_IS_CONSTRAINT (constraint));
+  g_return_if_fail (GTK_IS_CONSTRAINT_SOLVER (solver));
+  g_return_if_fail (ref != NULL);
+
+  constraint->constraint_ref = ref;
+  constraint->solver = solver;
+}
+
+void
+gtk_constraint_detach (GtkConstraint *constraint)
+{
+  g_return_if_fail (GTK_IS_CONSTRAINT (constraint));
+
+  if (constraint->constraint_ref == NULL)
+    return;
+
+  gtk_constraint_solver_remove_constraint (constraint->solver, constraint->constraint_ref);
+  constraint->constraint_ref = NULL;
+  constraint->solver = NULL;
+}
diff --git a/gtk/gtkconstraint.h b/gtk/gtkconstraint.h
new file mode 100644
index 0000000000..1ca9fd7f5e
--- /dev/null
+++ b/gtk/gtkconstraint.h
@@ -0,0 +1,85 @@
+/* gtkconstraint.h: Constraint between two widgets
+ * Copyright 2019  GNOME Foundation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Emmanuele Bassi
+ */
+
+#pragma once
+
+#include <gtk/gtktypes.h>
+#include <gtk/gtkenums.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CONSTRAINT (gtk_constraint_get_type ())
+
+/**
+ * GtkConstraint:
+ *
+ * An object describing the relation between two widget attributes.
+ *
+ * All relations are in the form:
+ *
+ * |[<!-- language=plain -->
+ *   target.attr_name = source.attr_name × multiplier + constant
+ * ]|
+ *
+ * A #GtkConstraint is immutable once it's created.
+ */
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GtkConstraint, gtk_constraint, GTK, CONSTRAINT, GObject)
+
+GDK_AVAILABLE_IN_ALL
+GtkConstraint *         gtk_constraint_new                      (GtkWidget              *target_widget,
+                                                                 GtkConstraintAttribute  target_attribute,
+                                                                 GtkConstraintRelation   relation,
+                                                                 GtkWidget              *source_widget,
+                                                                 GtkConstraintAttribute  source_attribute,
+                                                                 double                  multiplier,
+                                                                 double                  constant,
+                                                                 int                     strength);
+GDK_AVAILABLE_IN_ALL
+GtkConstraint *         gtk_constraint_new_constant             (GtkWidget              *target_widget,
+                                                                 GtkConstraintAttribute  target_attribute,
+                                                                 GtkConstraintRelation   relation,
+                                                                 double                  constant,
+                                                                 int                     strength);
+
+GDK_AVAILABLE_IN_ALL
+GtkWidget *             gtk_constraint_get_target_widget        (GtkConstraint          *constraint);
+GDK_AVAILABLE_IN_ALL
+GtkConstraintAttribute  gtk_constraint_get_target_attribute     (GtkConstraint          *constraint);
+GDK_AVAILABLE_IN_ALL
+GtkWidget *             gtk_constraint_get_source_widget        (GtkConstraint          *constraint);
+GDK_AVAILABLE_IN_ALL
+GtkConstraintAttribute  gtk_constraint_get_source_attribute     (GtkConstraint          *constraint);
+GDK_AVAILABLE_IN_ALL
+GtkConstraintRelation   gtk_constraint_get_relation             (GtkConstraint          *constraint);
+GDK_AVAILABLE_IN_ALL
+double                  gtk_constraint_get_multiplier           (GtkConstraint          *constraint);
+GDK_AVAILABLE_IN_ALL
+double                  gtk_constraint_get_constant             (GtkConstraint          *constraint);
+GDK_AVAILABLE_IN_ALL
+int                     gtk_constraint_get_strength             (GtkConstraint          *constraint);
+
+GDK_AVAILABLE_IN_ALL
+gboolean                gtk_constraint_is_required              (GtkConstraint          *constraint);
+GDK_AVAILABLE_IN_ALL
+gboolean                gtk_constraint_is_attached              (GtkConstraint          *constraint);
+GDK_AVAILABLE_IN_ALL
+gboolean                gtk_constraint_is_constant              (GtkConstraint          *constraint);
+
+G_END_DECLS
diff --git a/gtk/gtkconstraintlayout.c b/gtk/gtkconstraintlayout.c
new file mode 100644
index 0000000000..6cd6a7c70e
--- /dev/null
+++ b/gtk/gtkconstraintlayout.c
@@ -0,0 +1,1108 @@
+/* gtkconstraintlayout.c: Layout manager using constraints
+ * Copyright 2019  GNOME Foundation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Emmanuele Bassi
+ */
+
+/**
+ * SECTION: gtkconstraintlayout
+ * @Title: GtkConstraintLayout
+ * @Short_description: A layout manager using constraints
+ *
+ * GtkConstraintLayout is a layout manager that uses relations between
+ * widget attributes, expressed via #GtkConstraint instances, to measure
+ * and allocate widgets.
+ *
+ * # How do constraints work
+ *
+ * Constraints are objects defining the relationship between attributes
+ * of a widget; you can read the description of the #GtkConstraint
+ * class to have a more in depth definition.
+ *
+ * By taking multiple constraints and applying them to the children of
+ * a widget using #GtkConstraintLayout, it's possible to describe complex
+ * layout policies; each constraint applied to a child or to the parent
+ * widgets contributes to the full description of the layout, in terms of
+ * parameters for resolving the value of each attribute.
+ *
+ * It is important to note that a layout is defined by the totality of
+ * constraints; removing a child, or a constraint, from an existing layout
+ * without changing the remaining constraints may result in an unstable
+ * or unsolvable layout.
+ *
+ * Constraints have an implicit "reading order"; you should start describing
+ * each edge of each child, as well as their relationship with the parent
+ * container, from the top left (or top right, in RTL languages), horizontally
+ * first, and then vertically.
+ *
+ * A constraint-based layout with too few constraints can become "unstable",
+ * that is: have more than one solution. The behavior of an unstable layout
+ * is undefined.
+ *
+ * A constraint-based layout with conflicting constraints may be unsolvable,
+ * and lead to an unstable layout.
+ *
+ */
+
+#include "config.h"
+
+#include "gtkconstraintlayout.h"
+
+#include "gtkconstraintprivate.h"
+#include "gtkconstraintexpressionprivate.h"
+#include "gtkconstraintsolverprivate.h"
+#include "gtklayoutchild.h"
+
+#include "gtkdebug.h"
+#include "gtkintl.h"
+#include "gtkprivate.h"
+#include "gtksizerequest.h"
+#include "gtkwidgetprivate.h"
+
+struct _GtkConstraintLayoutChild
+{
+  GtkLayoutChild parent_instance;
+
+  /* HashTable<static string, Variable>; a hash table of variables,
+   * one for each attribute; we use these to query and suggest the
+   * values for the solver. The string is static and does not need
+   * to be freed.
+   */
+  GHashTable *bound_attributes;
+
+  /* Internal constraints on minimum and natural sizes */
+  GtkConstraintRef *width_constraint[2];
+  GtkConstraintRef *height_constraint[2];
+};
+
+struct _GtkConstraintLayout
+{
+  GtkLayoutManager parent_instance;
+
+  /* A pointer to the GtkConstraintSolver used by the layout manager;
+   * we acquire one when the layout manager gets rooted, and release
+   * it when it gets unrooted.
+   */
+  GtkConstraintSolver *solver;
+
+  /* HashTable<static string, Variable>; a hash table of variables,
+   * one for each attribute; we use these to query and suggest the
+   * values for the solver. The string is static and does not need
+   * to be freed.
+   */
+  GHashTable *bound_attributes;
+
+  /* HashSet<GtkConstraint>; the set of constraints on the
+   * parent widget, using the public API objects.
+   */
+  GHashTable *constraints;
+};
+
+G_DEFINE_TYPE (GtkConstraintLayoutChild, gtk_constraint_layout_child, GTK_TYPE_LAYOUT_CHILD)
+
+static inline GtkConstraintSolver *
+gtk_constraint_layout_get_solver (GtkConstraintLayout *self)
+{
+  GtkWidget *widget;
+  GtkRoot *root;
+
+  if (self->solver != NULL)
+    return self->solver;
+
+  widget = gtk_layout_manager_get_widget (GTK_LAYOUT_MANAGER (self));
+  if (widget == NULL)
+    return NULL;
+
+  root = gtk_widget_get_root (widget);
+  if (root == NULL)
+    return NULL;
+
+  self->solver = gtk_root_get_constraint_solver (root);
+
+  return self->solver;
+}
+
+static const char * const attribute_names[] = {
+  [GTK_CONSTRAINT_ATTRIBUTE_NONE]     = "none",
+  [GTK_CONSTRAINT_ATTRIBUTE_LEFT]     = "left",
+  [GTK_CONSTRAINT_ATTRIBUTE_RIGHT]    = "right",
+  [GTK_CONSTRAINT_ATTRIBUTE_TOP]      = "top",
+  [GTK_CONSTRAINT_ATTRIBUTE_BOTTOM]   = "bottom",
+  [GTK_CONSTRAINT_ATTRIBUTE_START]    = "start",
+  [GTK_CONSTRAINT_ATTRIBUTE_END]      = "end",
+  [GTK_CONSTRAINT_ATTRIBUTE_WIDTH]    = "width",
+  [GTK_CONSTRAINT_ATTRIBUTE_HEIGHT]   = "height",
+  [GTK_CONSTRAINT_ATTRIBUTE_CENTER_X] = "center-x",
+  [GTK_CONSTRAINT_ATTRIBUTE_CENTER_Y] = "center-y",
+  [GTK_CONSTRAINT_ATTRIBUTE_BASELINE] = "baseline",
+};
+
+G_GNUC_PURE
+static const char *
+get_attribute_name (GtkConstraintAttribute attr)
+{
+  return attribute_names[attr];
+}
+
+static GtkConstraintVariable *
+get_child_attribute (GtkConstraintLayoutChild *self,
+                     GtkConstraintSolver      *solver,
+                     GtkWidget                *widget,
+                     GtkConstraintAttribute    attr)
+{
+  GtkTextDirection text_dir;
+  const char *attr_name;
+  GtkConstraintVariable *res;
+
+  g_assert (attr != GTK_CONSTRAINT_ATTRIBUTE_NONE);
+
+  /* Resolve the start/end attributes depending on the layout's text direction */
+  if (attr == GTK_CONSTRAINT_ATTRIBUTE_START)
+    {
+      text_dir = gtk_widget_get_direction (widget);
+      if (text_dir == GTK_TEXT_DIR_RTL)
+        attr = GTK_CONSTRAINT_ATTRIBUTE_RIGHT;
+      else
+        attr = GTK_CONSTRAINT_ATTRIBUTE_LEFT;
+    }
+  else if (attr == GTK_CONSTRAINT_ATTRIBUTE_END)
+    {
+      text_dir = gtk_widget_get_direction (widget);
+      if (text_dir == GTK_TEXT_DIR_RTL)
+        attr = GTK_CONSTRAINT_ATTRIBUTE_LEFT;
+      else
+        attr = GTK_CONSTRAINT_ATTRIBUTE_RIGHT;
+    }
+
+  attr_name = get_attribute_name (attr);
+  res = g_hash_table_lookup (self->bound_attributes, attr_name);
+  if (res != NULL)
+    return res;
+
+  res = gtk_constraint_solver_create_variable (solver,
+                                               gtk_widget_get_name (widget),
+                                               attr_name,
+                                               0.0);
+  g_hash_table_insert (self->bound_attributes, (gpointer) attr_name, res);
+
+  /* Some attributes are really constraints computed from other
+   * attributes, to avoid creating additional constraints from
+   * the user's perspective
+   */
+  switch (attr)
+    {
+    /* right = left + width */
+    case GTK_CONSTRAINT_ATTRIBUTE_RIGHT:
+      {
+        GtkConstraintExpressionBuilder builder;
+        GtkConstraintVariable *left, *width;
+        GtkConstraintExpression *expr;
+
+        left = get_child_attribute (self, solver, widget, GTK_CONSTRAINT_ATTRIBUTE_LEFT);
+        width = get_child_attribute (self, solver, widget, GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
+
+        gtk_constraint_expression_builder_init (&builder, solver);
+        gtk_constraint_expression_builder_term (&builder, left);
+        gtk_constraint_expression_builder_plus (&builder);
+        gtk_constraint_expression_builder_term (&builder, width);
+        expr = gtk_constraint_expression_builder_finish (&builder);
+
+        gtk_constraint_solver_add_constraint (solver,
+                                              res, GTK_CONSTRAINT_RELATION_EQ, expr,
+                                              GTK_CONSTRAINT_WEIGHT_MEDIUM);
+      }
+      break;
+
+    /* bottom = top + height */
+    case GTK_CONSTRAINT_ATTRIBUTE_BOTTOM:
+      {
+        GtkConstraintExpressionBuilder builder;
+        GtkConstraintVariable *top, *height;
+        GtkConstraintExpression *expr;
+
+        top = get_child_attribute (self, solver, widget, GTK_CONSTRAINT_ATTRIBUTE_TOP);
+        height = get_child_attribute (self, solver, widget, GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
+
+        gtk_constraint_expression_builder_init (&builder, solver);
+        gtk_constraint_expression_builder_term (&builder, top);
+        gtk_constraint_expression_builder_plus (&builder);
+        gtk_constraint_expression_builder_term (&builder, height);
+        expr = gtk_constraint_expression_builder_finish (&builder);
+
+        gtk_constraint_solver_add_constraint (solver,
+                                              res, GTK_CONSTRAINT_RELATION_EQ, expr,
+                                              GTK_CONSTRAINT_WEIGHT_MEDIUM);
+      }
+      break;
+
+    /* centerX = (width / 2.0) + left*/
+    case GTK_CONSTRAINT_ATTRIBUTE_CENTER_X:
+      {
+        GtkConstraintExpressionBuilder builder;
+        GtkConstraintVariable *left, *width;
+        GtkConstraintExpression *expr;
+
+        left = get_child_attribute (self, solver, widget, GTK_CONSTRAINT_ATTRIBUTE_LEFT);
+        width = get_child_attribute (self, solver, widget, GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
+
+        gtk_constraint_expression_builder_init (&builder, solver);
+        gtk_constraint_expression_builder_term (&builder, width);
+        gtk_constraint_expression_builder_divide_by (&builder);
+        gtk_constraint_expression_builder_constant (&builder, 2.0);
+        gtk_constraint_expression_builder_plus (&builder);
+        gtk_constraint_expression_builder_term (&builder, left);
+        expr = gtk_constraint_expression_builder_finish (&builder);
+
+        gtk_constraint_solver_add_constraint (solver,
+                                              res, GTK_CONSTRAINT_RELATION_EQ, expr,
+                                              GTK_CONSTRAINT_WEIGHT_REQUIRED);
+      }
+      break;
+
+    /* centerY = (height / 2.0) + top */
+    case GTK_CONSTRAINT_ATTRIBUTE_CENTER_Y:
+      {
+        GtkConstraintExpressionBuilder builder;
+        GtkConstraintVariable *top, *height;
+        GtkConstraintExpression *expr;
+
+        top = get_child_attribute (self, solver, widget, GTK_CONSTRAINT_ATTRIBUTE_TOP);
+        height = get_child_attribute (self, solver, widget, GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
+
+        gtk_constraint_expression_builder_init (&builder, solver);
+        gtk_constraint_expression_builder_term (&builder, height);
+        gtk_constraint_expression_builder_divide_by (&builder);
+        gtk_constraint_expression_builder_constant (&builder, 2.0);
+        gtk_constraint_expression_builder_plus (&builder);
+        gtk_constraint_expression_builder_term (&builder, top);
+        expr = gtk_constraint_expression_builder_finish (&builder);
+
+        gtk_constraint_solver_add_constraint (solver,
+                                              res, GTK_CONSTRAINT_RELATION_EQ, expr,
+                                              GTK_CONSTRAINT_WEIGHT_REQUIRED);
+      }
+      break;
+
+    /* We do not allow negative sizes */
+    case GTK_CONSTRAINT_ATTRIBUTE_WIDTH:
+    case GTK_CONSTRAINT_ATTRIBUTE_HEIGHT:
+      {
+        GtkConstraintExpression *expr;
+
+        expr = gtk_constraint_expression_new (0.0);
+        gtk_constraint_solver_add_constraint (solver,
+                                              res, GTK_CONSTRAINT_RELATION_GE, expr,
+                                              GTK_CONSTRAINT_WEIGHT_REQUIRED);
+      }
+      break;
+
+    /* These are "pure" attributes */
+    case GTK_CONSTRAINT_ATTRIBUTE_NONE:
+    case GTK_CONSTRAINT_ATTRIBUTE_LEFT:
+    case GTK_CONSTRAINT_ATTRIBUTE_TOP:
+    case GTK_CONSTRAINT_ATTRIBUTE_BASELINE:
+      break;
+
+    /* These attributes must have been resolved to their real names */
+    case GTK_CONSTRAINT_ATTRIBUTE_START:
+    case GTK_CONSTRAINT_ATTRIBUTE_END:
+      g_assert_not_reached ();
+      break;
+
+    default:
+      break;
+    }
+
+  return res;
+}
+
+static void
+gtk_constraint_layout_child_finalize (GObject *gobject)
+{
+  GtkConstraintLayoutChild *self = GTK_CONSTRAINT_LAYOUT_CHILD (gobject);
+  GtkLayoutManager *manager;
+  GtkConstraintSolver *solver;
+
+  manager = gtk_layout_child_get_layout_manager (GTK_LAYOUT_CHILD (self));
+  solver = gtk_constraint_layout_get_solver (GTK_CONSTRAINT_LAYOUT (manager));
+  if (solver != NULL)
+    {
+      if (self->width_constraint[0] != NULL)
+        gtk_constraint_solver_remove_constraint (solver, self->width_constraint[0]);
+      if (self->width_constraint[1] != NULL)
+        gtk_constraint_solver_remove_constraint (solver, self->width_constraint[1]);
+      if (self->height_constraint[0] != NULL)
+        gtk_constraint_solver_remove_constraint (solver, self->height_constraint[0]);
+      if (self->height_constraint[1] != NULL)
+        gtk_constraint_solver_remove_constraint (solver, self->height_constraint[1]);
+    }
+
+  g_clear_pointer (&self->bound_attributes, g_hash_table_unref);
+
+  G_OBJECT_CLASS (gtk_constraint_layout_child_parent_class)->finalize (gobject);
+}
+
+static void
+gtk_constraint_layout_child_class_init (GtkConstraintLayoutChildClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize = gtk_constraint_layout_child_finalize;
+}
+
+static void
+gtk_constraint_layout_child_init (GtkConstraintLayoutChild *self)
+{
+  self->bound_attributes =
+    g_hash_table_new_full (g_str_hash, g_str_equal,
+                           NULL,
+                           (GDestroyNotify) gtk_constraint_variable_unref);
+}
+
+G_DEFINE_TYPE (GtkConstraintLayout, gtk_constraint_layout, GTK_TYPE_LAYOUT_MANAGER)
+
+static void
+gtk_constraint_layout_finalize (GObject *gobject)
+{
+  GtkConstraintLayout *self = GTK_CONSTRAINT_LAYOUT (gobject);
+
+  g_clear_pointer (&self->bound_attributes, g_hash_table_unref);
+  g_clear_pointer (&self->constraints, g_hash_table_unref);
+
+  G_OBJECT_CLASS (gtk_constraint_layout_parent_class)->finalize (gobject);
+}
+
+static GtkConstraintVariable *
+get_layout_attribute (GtkConstraintLayout    *self,
+                      GtkWidget              *widget,
+                      GtkConstraintAttribute  attr)
+{
+  GtkTextDirection text_dir;
+  const char *attr_name;
+  GtkConstraintVariable *res;
+
+  /* Resolve the start/end attributes depending on the layout's text direction */
+  if (attr == GTK_CONSTRAINT_ATTRIBUTE_START)
+    {
+      text_dir = gtk_widget_get_direction (widget);
+      if (text_dir == GTK_TEXT_DIR_RTL)
+        attr = GTK_CONSTRAINT_ATTRIBUTE_RIGHT;
+      else
+        attr = GTK_CONSTRAINT_ATTRIBUTE_LEFT;
+    }
+  else if (attr == GTK_CONSTRAINT_ATTRIBUTE_END)
+    {
+      text_dir = gtk_widget_get_direction (widget);
+      if (text_dir == GTK_TEXT_DIR_RTL)
+        attr = GTK_CONSTRAINT_ATTRIBUTE_LEFT;
+      else
+        attr = GTK_CONSTRAINT_ATTRIBUTE_RIGHT;
+    }
+
+  attr_name = get_attribute_name (attr);
+  res = g_hash_table_lookup (self->bound_attributes, attr_name);
+  if (res != NULL)
+    return res;
+
+  res = gtk_constraint_solver_create_variable (self->solver, "super", attr_name, 0.0);
+  g_hash_table_insert (self->bound_attributes, (gpointer) attr_name, res);
+
+  /* Some attributes are really constraints computed from other
+   * attributes, to avoid creating additional constraints from
+   * the user's perspective
+   */
+  switch (attr)
+    {
+    /* right = left + width */
+    case GTK_CONSTRAINT_ATTRIBUTE_RIGHT:
+      {
+        GtkConstraintExpressionBuilder builder;
+        GtkConstraintVariable *left, *width;
+        GtkConstraintExpression *expr;
+
+        left = get_layout_attribute (self, widget, GTK_CONSTRAINT_ATTRIBUTE_LEFT);
+        width = get_layout_attribute (self, widget, GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
+
+        gtk_constraint_expression_builder_init (&builder, self->solver);
+        gtk_constraint_expression_builder_term (&builder, left);
+        gtk_constraint_expression_builder_plus (&builder);
+        gtk_constraint_expression_builder_term (&builder, width);
+        expr = gtk_constraint_expression_builder_finish (&builder);
+
+        gtk_constraint_solver_add_constraint (self->solver,
+                                              res, GTK_CONSTRAINT_RELATION_EQ, expr,
+                                              GTK_CONSTRAINT_WEIGHT_REQUIRED);
+      }
+      break;
+
+    /* bottom = top + height */
+    case GTK_CONSTRAINT_ATTRIBUTE_BOTTOM:
+      {
+        GtkConstraintExpressionBuilder builder;
+        GtkConstraintVariable *top, *height;
+        GtkConstraintExpression *expr;
+
+        top = get_layout_attribute (self, widget, GTK_CONSTRAINT_ATTRIBUTE_TOP);
+        height = get_layout_attribute (self, widget, GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
+
+        gtk_constraint_expression_builder_init (&builder, self->solver);
+        gtk_constraint_expression_builder_term (&builder, top);
+        gtk_constraint_expression_builder_plus (&builder);
+        gtk_constraint_expression_builder_term (&builder, height);
+        expr = gtk_constraint_expression_builder_finish (&builder);
+
+        gtk_constraint_solver_add_constraint (self->solver,
+                                              res, GTK_CONSTRAINT_RELATION_EQ, expr,
+                                              GTK_CONSTRAINT_WEIGHT_REQUIRED);
+      }
+      break;
+
+    /* centerX = left + (width / 2.0) */
+    case GTK_CONSTRAINT_ATTRIBUTE_CENTER_X:
+      {
+        GtkConstraintExpressionBuilder builder;
+        GtkConstraintVariable *left, *width;
+        GtkConstraintExpression *expr;
+
+        left = get_layout_attribute (self, widget, GTK_CONSTRAINT_ATTRIBUTE_LEFT);
+        width = get_layout_attribute (self, widget, GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
+
+        gtk_constraint_expression_builder_init (&builder, self->solver);
+        gtk_constraint_expression_builder_term (&builder, width);
+        gtk_constraint_expression_builder_divide_by (&builder);
+        gtk_constraint_expression_builder_constant (&builder, 2.0);
+        gtk_constraint_expression_builder_plus (&builder);
+        gtk_constraint_expression_builder_term (&builder, left);
+        expr = gtk_constraint_expression_builder_finish (&builder);
+
+        gtk_constraint_solver_add_constraint (self->solver,
+                                              res, GTK_CONSTRAINT_RELATION_EQ, expr,
+                                              GTK_CONSTRAINT_WEIGHT_REQUIRED);
+      }
+      break;
+
+    /* centerY = top + (height / 2.0) */
+    case GTK_CONSTRAINT_ATTRIBUTE_CENTER_Y:
+      {
+        GtkConstraintExpressionBuilder builder;
+        GtkConstraintVariable *top, *height;
+        GtkConstraintExpression *expr;
+
+        top = get_layout_attribute (self, widget, GTK_CONSTRAINT_ATTRIBUTE_TOP);
+        height = get_layout_attribute (self, widget, GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
+
+        gtk_constraint_expression_builder_init (&builder, self->solver);
+        gtk_constraint_expression_builder_term (&builder, height);
+        gtk_constraint_expression_builder_divide_by (&builder);
+        gtk_constraint_expression_builder_constant (&builder, 2.0);
+        gtk_constraint_expression_builder_plus (&builder);
+        gtk_constraint_expression_builder_term (&builder, top);
+        expr = gtk_constraint_expression_builder_finish (&builder);
+
+        gtk_constraint_solver_add_constraint (self->solver,
+                                              res, GTK_CONSTRAINT_RELATION_EQ, expr,
+                                              GTK_CONSTRAINT_WEIGHT_REQUIRED);
+      }
+      break;
+
+    /* We do not allow negative sizes */
+    case GTK_CONSTRAINT_ATTRIBUTE_WIDTH:
+    case GTK_CONSTRAINT_ATTRIBUTE_HEIGHT:
+      {
+        GtkConstraintExpression *expr;
+
+        expr = gtk_constraint_expression_new (0.0);
+        gtk_constraint_solver_add_constraint (self->solver,
+                                              res, GTK_CONSTRAINT_RELATION_GE, expr,
+                                              GTK_CONSTRAINT_WEIGHT_REQUIRED);
+      }
+      break;
+
+    /* These are "pure" attributes */
+    case GTK_CONSTRAINT_ATTRIBUTE_NONE:
+    case GTK_CONSTRAINT_ATTRIBUTE_LEFT:
+    case GTK_CONSTRAINT_ATTRIBUTE_TOP:
+    case GTK_CONSTRAINT_ATTRIBUTE_BASELINE:
+      break;
+
+    /* These attributes must have been resolved to their real names */
+    case GTK_CONSTRAINT_ATTRIBUTE_START:
+    case GTK_CONSTRAINT_ATTRIBUTE_END:
+      g_assert_not_reached ();
+      break;
+
+    default:
+      break;
+    }
+
+  return res;
+}
+
+/*< private >
+ * layout_add_constraint:
+ * @self: a #GtkConstraintLayout
+ * @constraint: a #GtkConstraint
+ *
+ * Turns a #GtkConstraint into a #GtkConstraintRef inside the
+ * constraint solver associated to @self.
+ *
+ * If @self does not have a #GtkConstraintSolver, because it
+ * has not been rooted yet, we just store the @constraint instance,
+ * and we're going to call this function when the layout manager
+ * gets rooted.
+ */
+static void
+layout_add_constraint (GtkConstraintLayout *self,
+                       GtkConstraint       *constraint)
+{
+  GtkConstraintVariable *target_attr, *source_attr;
+  GtkConstraintExpressionBuilder builder;
+  GtkConstraintExpression *expr;
+  GtkConstraintSolver *solver;
+  GtkConstraintAttribute attr;
+  GtkWidget *target_widget, *source_widget;
+  GtkWidget *layout_widget;
+
+  if (gtk_constraint_is_attached (constraint))
+    return;
+
+  /* Once we pass the preconditions, we check if we can turn a GtkConstraint
+   * into a GtkConstraintRef; if we can't, we keep a reference to the
+   * constraint object and try later on
+   */
+  layout_widget = gtk_layout_manager_get_widget (GTK_LAYOUT_MANAGER (self));
+  if (layout_widget == NULL)
+    return;
+
+  solver = gtk_constraint_layout_get_solver (self);
+  if (solver == NULL)
+    return;
+
+  attr = gtk_constraint_get_target_attribute (constraint);
+  target_widget = gtk_constraint_get_target_widget (constraint);
+  if (target_widget == NULL || target_widget == layout_widget)
+    {
+      /* A NULL target widget is assumed to be referring to the layout itself */
+      target_attr = get_layout_attribute (self, layout_widget, attr);
+    }
+  else if (gtk_widget_get_parent (target_widget) == layout_widget)
+    {
+      GtkLayoutChild *child_info;
+
+      child_info = gtk_layout_manager_get_layout_child (GTK_LAYOUT_MANAGER (self), target_widget);
+      target_attr = get_child_attribute (GTK_CONSTRAINT_LAYOUT_CHILD (child_info),
+                                         solver,
+                                         target_widget,
+                                         attr);
+    }
+  else
+    {
+      g_critical ("Unknown target widget '%s'", gtk_widget_get_name (target_widget)); 
+      target_attr = NULL;
+    }
+
+  if (target_attr == NULL)
+    return;
+
+  attr = gtk_constraint_get_source_attribute (constraint);
+  source_widget = gtk_constraint_get_source_widget (constraint);
+
+  /* The constraint is a constant */
+  if (attr == GTK_CONSTRAINT_ATTRIBUTE_NONE)
+    {
+      source_attr = NULL;
+    }
+  else
+    {
+      if (source_widget == NULL || source_widget == layout_widget)
+        {
+          source_attr = get_layout_attribute (self, layout_widget, attr);
+        }
+      else if (gtk_widget_get_parent (source_widget) == layout_widget)
+        {
+          GtkLayoutChild *child_info;
+
+          child_info = gtk_layout_manager_get_layout_child (GTK_LAYOUT_MANAGER (self), source_widget);
+          source_attr = get_child_attribute (GTK_CONSTRAINT_LAYOUT_CHILD (child_info),
+                                             solver,
+                                             source_widget,
+                                             attr);
+        }
+      else
+        {
+          g_critical ("Unknown source widget '%s'", gtk_widget_get_name (source_widget));
+          source_attr = NULL;
+          return;
+        }
+     }
+
+  /* Build the expression */
+  gtk_constraint_expression_builder_init (&builder, self->solver);
+
+  if (source_attr != NULL)
+    {
+      gtk_constraint_expression_builder_term (&builder, source_attr);
+      gtk_constraint_expression_builder_multiply_by (&builder);
+      gtk_constraint_expression_builder_constant (&builder, gtk_constraint_get_multiplier (constraint));
+      gtk_constraint_expression_builder_plus (&builder);
+    }
+
+  gtk_constraint_expression_builder_constant (&builder, gtk_constraint_get_constant (constraint));
+  expr = gtk_constraint_expression_builder_finish (&builder);
+
+  constraint->solver = solver;
+  constraint->constraint_ref =
+    gtk_constraint_solver_add_constraint (self->solver,
+                                          target_attr,
+                                          gtk_constraint_get_relation (constraint),
+                                          expr,
+                                          gtk_constraint_get_weight (constraint));
+}
+
+static void
+gtk_constraint_layout_measure (GtkLayoutManager *manager,
+                               GtkWidget        *widget,
+                               GtkOrientation    orientation,
+                               int               for_size,
+                               int              *minimum,
+                               int              *natural,
+                               int              *minimum_baseline,
+                               int              *natural_baseline)
+{
+  GtkConstraintLayout *self = GTK_CONSTRAINT_LAYOUT (manager);
+  GtkConstraintVariable *size, *opposite_size;
+  GtkConstraintSolver *solver;
+  GtkWidget *child;
+  int value;
+
+  solver = gtk_constraint_layout_get_solver (self);
+  if (solver == NULL)
+    return;
+
+  /* We measure each child in the layout and impose restrictions on the
+   * minimum and natural size, so we can solve the size of the overall
+   * layout later on
+   */
+  for (child = _gtk_widget_get_first_child (widget);
+       child != NULL;
+       child = _gtk_widget_get_next_sibling (child))
+    {
+      GtkConstraintLayoutChild *child_info;
+      GtkConstraintVariable *width_var, *height_var;
+      int min_size = 0, nat_size = 0;
+
+      if (!gtk_widget_should_layout (child))
+        continue;
+
+      child_info = GTK_CONSTRAINT_LAYOUT_CHILD (gtk_layout_manager_get_layout_child (manager, child));
+
+      gtk_widget_measure (child, orientation, for_size,
+                          &min_size, &nat_size,
+                          NULL, NULL);
+
+      if (child_info->width_constraint[0] != NULL)
+        {
+          gtk_constraint_solver_remove_constraint (solver, child_info->width_constraint[0]);
+          gtk_constraint_solver_remove_constraint (solver, child_info->width_constraint[1]);
+        }
+
+      if (child_info->height_constraint[0] != NULL)
+        {
+          gtk_constraint_solver_remove_constraint (solver, child_info->height_constraint[0]);
+          gtk_constraint_solver_remove_constraint (solver, child_info->height_constraint[1]);
+        }
+
+      switch (orientation)
+        {
+        case GTK_ORIENTATION_HORIZONTAL:
+          width_var = get_child_attribute (child_info, solver, child,
+                                           GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
+
+          child_info->width_constraint[0] =
+            gtk_constraint_solver_add_constraint (solver,
+                                                  width_var,
+                                                  GTK_CONSTRAINT_RELATION_GE,
+                                                  gtk_constraint_expression_new (min_size),
+                                                GTK_CONSTRAINT_WEIGHT_REQUIRED);
+          child_info->width_constraint[1] =
+            gtk_constraint_solver_add_constraint (solver,
+                                                  width_var,
+                                                  GTK_CONSTRAINT_RELATION_EQ,
+                                                  gtk_constraint_expression_new (nat_size),
+                                                  GTK_CONSTRAINT_WEIGHT_MEDIUM);
+          break;
+
+        case GTK_ORIENTATION_VERTICAL:
+          height_var = get_child_attribute (child_info, solver, child,
+                                            GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
+
+          child_info->height_constraint[0] =
+            gtk_constraint_solver_add_constraint (solver,
+                                                  height_var,
+                                                  GTK_CONSTRAINT_RELATION_GE,
+                                                  gtk_constraint_expression_new (min_size),
+                                                  GTK_CONSTRAINT_WEIGHT_REQUIRED);
+          child_info->height_constraint[1] =
+            gtk_constraint_solver_add_constraint (solver,
+                                                  height_var,
+                                                  GTK_CONSTRAINT_RELATION_EQ,
+                                                  gtk_constraint_expression_new (nat_size),
+                                                  GTK_CONSTRAINT_WEIGHT_MEDIUM);
+          break;
+
+        default:
+          break;
+        }
+    }
+
+  switch (orientation)
+    {
+    case GTK_ORIENTATION_HORIZONTAL:
+      size = get_layout_attribute (self, widget, GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
+      opposite_size = get_layout_attribute (self, widget, GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
+      break;
+
+    case GTK_ORIENTATION_VERTICAL:
+      size = get_layout_attribute (self, widget, GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
+      opposite_size = get_layout_attribute (self, widget, GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+
+  g_assert (size != NULL && opposite_size != NULL);
+
+  /* We impose a temporary value on the size and opposite size of the
+   * layout, with a low weight to let the solver settle towards the
+   * natural state of the system. Once we get the value out, we can
+   * remove these constraints
+   */
+  gtk_constraint_solver_add_edit_variable (solver, size, GTK_CONSTRAINT_WEIGHT_WEAK + 1);
+  gtk_constraint_solver_add_edit_variable (solver, opposite_size, GTK_CONSTRAINT_WEIGHT_WEAK + 2);
+
+  gtk_constraint_solver_begin_edit (solver);
+
+  gtk_constraint_solver_suggest_value (solver, size, 0.0);
+  gtk_constraint_solver_suggest_value (solver, opposite_size, for_size >= 0 ? for_size : 0.0);
+
+  gtk_constraint_solver_resolve (solver);
+
+  GTK_NOTE (LAYOUT,
+            g_print ("layout %p preferred %s size: %.3f (for opposite size: %d)\n",
+                     self,
+                     orientation == GTK_ORIENTATION_HORIZONTAL ? "horizontal" : "vertical",
+                     gtk_constraint_variable_get_value (size),
+                     for_size));
+
+  value = gtk_constraint_variable_get_value (size);
+
+  gtk_constraint_solver_remove_edit_variable (solver, size);
+  gtk_constraint_solver_remove_edit_variable (solver, opposite_size);
+
+  gtk_constraint_solver_end_edit (solver);
+
+  if (minimum != NULL)
+    *minimum = value;
+
+  if (natural != NULL)
+    *natural = value;
+}
+
+static void
+gtk_constraint_layout_allocate (GtkLayoutManager *manager,
+                                GtkWidget        *widget,
+                                int               width,
+                                int               height,
+                                int               baseline)
+{
+  GtkConstraintLayout *self = GTK_CONSTRAINT_LAYOUT (manager);
+  GtkConstraintRef *stay_w, *stay_h, *stay_t, *stay_l;
+  GtkConstraintSolver *solver;
+  GtkConstraintVariable *layout_top, *layout_height;
+  GtkConstraintVariable *layout_left, *layout_width;
+  GtkWidget *child;
+
+  solver = gtk_constraint_layout_get_solver (self);
+  if (solver == NULL)
+    return;
+
+  /* We add required stay constraints to ensure that the layout remains
+   * within the bounds of the allocation
+   */
+  layout_top = get_layout_attribute (self, widget, GTK_CONSTRAINT_ATTRIBUTE_TOP);
+  layout_left = get_layout_attribute (self, widget, GTK_CONSTRAINT_ATTRIBUTE_LEFT);
+  layout_width = get_layout_attribute (self, widget, GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
+  layout_height = get_layout_attribute (self, widget, GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
+
+  gtk_constraint_variable_set_value (layout_top, 0.0);
+  stay_t = gtk_constraint_solver_add_stay_variable (solver,
+                                                    layout_top,
+                                                    GTK_CONSTRAINT_WEIGHT_REQUIRED);
+  gtk_constraint_variable_set_value (layout_left, 0.0);
+  stay_l = gtk_constraint_solver_add_stay_variable (solver,
+                                                    layout_left,
+                                                    GTK_CONSTRAINT_WEIGHT_REQUIRED);
+  gtk_constraint_variable_set_value (layout_width, width);
+  stay_w = gtk_constraint_solver_add_stay_variable (solver,
+                                                    layout_width,
+                                                    GTK_CONSTRAINT_WEIGHT_REQUIRED);
+  gtk_constraint_variable_set_value (layout_height, height);
+  stay_h = gtk_constraint_solver_add_stay_variable (solver,
+                                                    layout_height,
+                                                    GTK_CONSTRAINT_WEIGHT_REQUIRED);
+  GTK_NOTE (LAYOUT,
+            g_print ("Layout [%p]: { .x: %g, .y: %g, .w: %g, .h: %g }\n",
+                     self,
+                     gtk_constraint_variable_get_value (layout_left),
+                     gtk_constraint_variable_get_value (layout_top),
+                     gtk_constraint_variable_get_value (layout_width),
+                     gtk_constraint_variable_get_value (layout_height)));
+
+  /* We reset the constraints on the size of each child, so we are sure the
+   * layout is up to date
+   */
+  for (child = _gtk_widget_get_first_child (widget);
+       child != NULL;
+       child = _gtk_widget_get_next_sibling (child))
+    {
+      GtkConstraintLayoutChild *child_info;
+      GtkConstraintVariable *width_var, *height_var;
+      GtkRequisition min_req, nat_req;
+
+      if (!gtk_widget_should_layout (child))
+        continue;
+
+      child_info = GTK_CONSTRAINT_LAYOUT_CHILD (gtk_layout_manager_get_layout_child (manager, child));
+
+      gtk_widget_get_preferred_size (child, &min_req, &nat_req);
+
+      if (child_info->width_constraint[0] != NULL)
+        {
+          gtk_constraint_solver_remove_constraint (solver, child_info->width_constraint[0]);
+          gtk_constraint_solver_remove_constraint (solver, child_info->width_constraint[1]);
+        }
+
+      if (child_info->height_constraint[0] != NULL)
+        {
+          gtk_constraint_solver_remove_constraint (solver, child_info->height_constraint[0]);
+          gtk_constraint_solver_remove_constraint (solver, child_info->height_constraint[1]);
+        }
+
+      width_var = get_child_attribute (child_info, solver, child,
+                                       GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
+
+      child_info->width_constraint[0] =
+        gtk_constraint_solver_add_constraint (solver,
+                                              width_var,
+                                              GTK_CONSTRAINT_RELATION_GE,
+                                              gtk_constraint_expression_new (min_req.width),
+                                              GTK_CONSTRAINT_WEIGHT_REQUIRED);
+      child_info->width_constraint[1] =
+        gtk_constraint_solver_add_constraint (solver,
+                                              width_var,
+                                              GTK_CONSTRAINT_RELATION_EQ,
+                                              gtk_constraint_expression_new (nat_req.width),
+                                              GTK_CONSTRAINT_WEIGHT_MEDIUM);
+
+      height_var = get_child_attribute (child_info, solver, child,
+                                        GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
+
+      child_info->height_constraint[0] =
+        gtk_constraint_solver_add_constraint (solver,
+                                              height_var,
+                                              GTK_CONSTRAINT_RELATION_GE,
+                                              gtk_constraint_expression_new (min_req.height),
+                                              GTK_CONSTRAINT_WEIGHT_REQUIRED);
+      child_info->height_constraint[1] =
+        gtk_constraint_solver_add_constraint (solver,
+                                              height_var,
+                                              GTK_CONSTRAINT_RELATION_EQ,
+                                              gtk_constraint_expression_new (nat_req.height),
+                                              GTK_CONSTRAINT_WEIGHT_MEDIUM);
+    }
+
+  for (child = _gtk_widget_get_first_child (widget);
+       child != NULL;
+       child = _gtk_widget_get_next_sibling (child))
+    {
+      GtkConstraintVariable *var_top, *var_left, *var_width, *var_height;
+      GtkConstraintVariable *var_baseline;
+      GtkConstraintLayoutChild *child_info;
+      GtkAllocation child_alloc;
+      int child_baseline = -1;
+
+      if (!gtk_widget_should_layout (child))
+        continue;
+
+      child_info = GTK_CONSTRAINT_LAYOUT_CHILD (gtk_layout_manager_get_layout_child (manager, child));
+
+      /* Retrieve all the values associated with the child */
+      var_top = get_child_attribute (child_info, solver, child, GTK_CONSTRAINT_ATTRIBUTE_TOP);
+      var_left = get_child_attribute (child_info, solver, child, GTK_CONSTRAINT_ATTRIBUTE_LEFT);
+      var_width = get_child_attribute (child_info, solver, child, GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
+      var_height = get_child_attribute (child_info, solver, child, GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
+      var_baseline = get_child_attribute (child_info, solver, child, GTK_CONSTRAINT_ATTRIBUTE_BASELINE);
+
+      GTK_NOTE (LAYOUT,
+                g_print ("Allocating child '%s'[%p] with { .x: %g, .y: %g, .w: %g, .h: %g, .b: %g }\n",
+                         gtk_widget_get_name (child), child,
+                         gtk_constraint_variable_get_value (var_left),
+                         gtk_constraint_variable_get_value (var_top),
+                         gtk_constraint_variable_get_value (var_width),
+                         gtk_constraint_variable_get_value (var_height),
+                         gtk_constraint_variable_get_value (var_baseline)));
+
+      child_alloc.x = floor (gtk_constraint_variable_get_value (var_left));
+      child_alloc.y = floor (gtk_constraint_variable_get_value (var_top));
+      child_alloc.width = ceil (gtk_constraint_variable_get_value (var_width));
+      child_alloc.height = ceil (gtk_constraint_variable_get_value (var_height));
+
+      if (gtk_constraint_variable_get_value (var_baseline) > 0)
+        child_baseline = floor (gtk_constraint_variable_get_value (var_baseline));
+
+      gtk_widget_size_allocate (GTK_WIDGET (child),
+                                &child_alloc,
+                                child_baseline);
+    }
+
+  /* The allocation stay constraints are not needed any more */
+  gtk_constraint_solver_remove_constraint (solver, stay_w);
+  gtk_constraint_solver_remove_constraint (solver, stay_h);
+  gtk_constraint_solver_remove_constraint (solver, stay_t);
+  gtk_constraint_solver_remove_constraint (solver, stay_l);
+}
+
+static void
+gtk_constraint_layout_root (GtkLayoutManager *manager)
+{
+  GtkConstraintLayout *self = GTK_CONSTRAINT_LAYOUT (manager);
+  GHashTableIter iter;
+  GtkWidget *widget;
+  GtkRoot *root;
+  gpointer key;
+
+  widget = gtk_layout_manager_get_widget (manager);
+  root = gtk_widget_get_root (widget);
+
+  self->solver = gtk_root_get_constraint_solver (root);
+
+  /* Now that we have a solver, attach all constraints we have */
+  g_hash_table_iter_init (&iter, self->constraints);
+  while (g_hash_table_iter_next (&iter, &key, NULL))
+    {
+      GtkConstraint *constraint = key;
+
+      layout_add_constraint (self, constraint);
+    }
+}
+
+static void
+gtk_constraint_layout_unroot (GtkLayoutManager *manager)
+{
+  GtkConstraintLayout *self = GTK_CONSTRAINT_LAYOUT (manager);
+  GHashTableIter iter;
+  gpointer key;
+
+  /* Detach all constraints we're holding, as we're removing the layout
+   * from the global solver, and they should not contribute to the other
+   * layouts
+   */
+  g_hash_table_iter_init (&iter, self->constraints);
+  while (g_hash_table_iter_next (&iter, &key, NULL))
+    {
+      GtkConstraint *constraint = key;
+
+      gtk_constraint_detach (constraint);
+    }
+
+  self->solver = NULL;
+}
+
+static void
+gtk_constraint_layout_class_init (GtkConstraintLayoutClass *klass)
+{
+  GtkLayoutManagerClass *manager_class = GTK_LAYOUT_MANAGER_CLASS (klass);
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize = gtk_constraint_layout_finalize;
+
+  manager_class->layout_child_type = GTK_TYPE_CONSTRAINT_LAYOUT_CHILD;
+  manager_class->measure = gtk_constraint_layout_measure;
+  manager_class->allocate = gtk_constraint_layout_allocate;
+  manager_class->root = gtk_constraint_layout_root;
+  manager_class->unroot = gtk_constraint_layout_unroot;
+}
+
+static void
+gtk_constraint_layout_init (GtkConstraintLayout *self)
+{
+  /* The bound variables in the solver */
+  self->bound_attributes =
+    g_hash_table_new_full (g_str_hash, g_str_equal,
+                           NULL,
+                           (GDestroyNotify) gtk_constraint_variable_unref);
+
+  /* The GtkConstraint instances we own */
+  self->constraints =
+    g_hash_table_new_full (NULL, NULL,
+                           (GDestroyNotify) g_object_unref,
+                           NULL);
+}
+
+/**
+ * gtk_constraint_layout_new:
+ *
+ * Creates a new #GtkConstraintLayout layout manager.
+ *
+ * Returns: the newly created #GtkConstraintLayout
+ */
+GtkLayoutManager *
+gtk_constraint_layout_new (void)
+{
+  return g_object_new (GTK_TYPE_CONSTRAINT_LAYOUT, NULL);
+}
+
+/**
+ * gtk_constraint_layout_add_constraint:
+ * @manager: a #GtkConstraintLayout
+ * @constraint: (transfer full): a #GtkConstraint
+ *
+ * Adds a #GtkConstraint to the layout manager.
+ *
+ * The #GtkConstraint:source-widget and #GtkConstraint:target-widget
+ * properties of @constraint can be:
+ *
+ *  - set to %NULL to indicate that the constraint refers to the
+ *    widget using @manager
+ *  - set to the #GtkWidget using @manager
+ *  - set to a child of the #GtkWidget using @manager
+ *
+ * The @manager acquires the ownership of @constraint after calling
+ * this function.
+ */
+void
+gtk_constraint_layout_add_constraint (GtkConstraintLayout *manager,
+                                      GtkConstraint       *constraint)
+{
+  g_return_if_fail (GTK_IS_CONSTRAINT_LAYOUT (manager));
+  g_return_if_fail (GTK_IS_CONSTRAINT (constraint));
+  g_return_if_fail (!gtk_constraint_is_attached (constraint));
+
+  layout_add_constraint (manager, constraint);
+
+  g_hash_table_add (manager->constraints, constraint);
+}
diff --git a/gtk/gtkconstraintlayout.h b/gtk/gtkconstraintlayout.h
new file mode 100644
index 0000000000..bd6528da3b
--- /dev/null
+++ b/gtk/gtkconstraintlayout.h
@@ -0,0 +1,53 @@
+/* gtkconstraintlayout.h: Layout manager using constraints
+ * Copyright 2019  GNOME Foundation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Emmanuele Bassi
+ */
+#pragma once
+
+#include <gtk/gtklayoutmanager.h>
+#include <gtk/gtkconstraint.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CONSTRAINT_LAYOUT (gtk_constraint_layout_get_type ())
+#define GTK_TYPE_CONSTRAINT_LAYOUT_CHILD (gtk_constraint_layout_child_get_type ())
+
+/**
+ * GtkConstraintLayout:
+ *
+ * A layout manager using #GtkConstraint to describe
+ * relations between widgets.
+ */
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GtkConstraintLayout, gtk_constraint_layout, GTK, CONSTRAINT_LAYOUT, GtkLayoutManager)
+
+GDK_AVAILABLE_IN_ALL
+GtkLayoutManager *      gtk_constraint_layout_new               (void);
+
+GDK_AVAILABLE_IN_ALL
+void                    gtk_constraint_layout_add_constraint    (GtkConstraintLayout *manager,
+                                                                 GtkConstraint       *constraint);
+
+/**
+ * GtkConstraintLayoutChild:
+ *
+ * A #GtkLayoutChild in a #GtkConstraintLayout.
+ */
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GtkConstraintLayoutChild, gtk_constraint_layout_child, GTK, CONSTRAINT_LAYOUT_CHILD, 
GtkLayoutChild)
+
+G_END_DECLS
diff --git a/gtk/gtkconstraintprivate.h b/gtk/gtkconstraintprivate.h
new file mode 100644
index 0000000000..90f70234a2
--- /dev/null
+++ b/gtk/gtkconstraintprivate.h
@@ -0,0 +1,62 @@
+/* gtkconstraintprivate.h: Constraint between two widgets
+ * Copyright 2019  GNOME Foundation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Emmanuele Bassi
+ */
+
+#pragma once
+
+#include "gtkconstraint.h"
+#include "gtkconstrainttypesprivate.h"
+
+G_BEGIN_DECLS
+
+struct _GtkConstraint
+{
+  GObject parent_instance;
+
+  GtkConstraintAttribute target_attribute;
+  GtkConstraintAttribute source_attribute;
+
+  GtkWidget *target_widget;
+  GtkWidget *source_widget;
+
+  GtkConstraintRelation relation;
+
+  double multiplier;
+  double constant;
+
+  int strength;
+
+  /* A reference to the real constraint inside the
+   * GtkConstraintSolver, so we can remove it when
+   * finalizing the GtkConstraint instance
+   */
+  GtkConstraintRef *constraint_ref;
+
+  GtkConstraintSolver *solver;
+
+  guint active : 1;
+};
+
+double  gtk_constraint_get_weight       (GtkConstraint       *constraint);
+
+void    gtk_constraint_attach           (GtkConstraint       *constraint,
+                                         GtkConstraintSolver *solver,
+                                         GtkConstraintRef    *ref);
+void    gtk_constraint_detach           (GtkConstraint       *constraint);
+
+G_END_DECLS
diff --git a/gtk/gtkconstraintsolver.c b/gtk/gtkconstraintsolver.c
index 411b006957..53262b70af 100644
--- a/gtk/gtkconstraintsolver.c
+++ b/gtk/gtkconstraintsolver.c
@@ -348,37 +348,6 @@ gtk_constraint_solver_init (GtkConstraintSolver *self)
   self->auto_solve = TRUE;
 }
 
-/* Symbolic weight thresholds
- *
- * Constraint weights live on a continuum, but we use thresholds for simplicity's
- * sake, so we don't have to necessarily reason in terms of numeric values.
- *
- * The public API has a similar approach, where the symbolic constants are negative
- * values, and positive values are explicit weights. We map those values into
- * numeric values that the GtkConstraintSolver can plug into the linear equations
- * tableau.
- */
-#define GTK_CONSTRAINT_WEIGHT_REQUIRED  (make_weight (1000, 1000, 1000, 1))
-#define GTK_CONSTRAINT_WEIGHT_STRONG    (make_weight (   1,    0,    0, 1))
-#define GTK_CONSTRAINT_WEIGHT_MEDIUM    (make_weight (   0,    1,    0, 1))
-#define GTK_CONSTRAINT_WEIGHT_WEAK      (make_weight (   0,    0,    1, 1))
-
-G_GNUC_PURE
-static inline double
-make_weight (double a,
-             double b,
-             double c,
-             double w)
-{
-  double res = 0;
-
-  res += CLAMP (a * w, 0, 1000) * 1000000;
-  res += CLAMP (b * w, 0, 1000) * 1000;
-  res += CLAMP (c * w, 0, 1000);
-
-  return res;
-}
-
 static void
 gtk_constraint_ref_free (GtkConstraintRef *self)
 {
diff --git a/gtk/gtkconstraintsolverprivate.h b/gtk/gtkconstraintsolverprivate.h
index 807b0b08d6..69eed376c7 100644
--- a/gtk/gtkconstraintsolverprivate.h
+++ b/gtk/gtkconstraintsolverprivate.h
@@ -29,6 +29,37 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (GtkConstraintSolver, gtk_constraint_solver, GTK, CONSTRAINT_SOLVER, GObject)
 
+/* Symbolic weight thresholds
+ *
+ * Constraint weights live on a continuum, but we use thresholds for simplicity's
+ * sake, so we don't have to necessarily reason in terms of numeric values.
+ *
+ * The public API has a similar approach, where the symbolic constants are negative
+ * values, and positive values are explicit weights. We map those values into
+ * numeric values that the GtkConstraintSolver can plug into the linear equations
+ * tableau.
+ */
+#define GTK_CONSTRAINT_WEIGHT_REQUIRED  (make_weight (1000, 1000, 1000, 1))
+#define GTK_CONSTRAINT_WEIGHT_STRONG    (make_weight (   1,    0,    0, 1))
+#define GTK_CONSTRAINT_WEIGHT_MEDIUM    (make_weight (   0,    1,    0, 1))
+#define GTK_CONSTRAINT_WEIGHT_WEAK      (make_weight (   0,    0,    1, 1))
+
+G_GNUC_PURE
+static inline double
+make_weight (double a,
+             double b,
+             double c,
+             double w)
+{
+  double res = 0;
+
+  res += CLAMP (a * w, 0, 1000) * 1000000;
+  res += CLAMP (b * w, 0, 1000) * 1000;
+  res += CLAMP (c * w, 0, 1000);
+
+  return res;
+}
+
 GtkConstraintSolver *
 gtk_constraint_solver_new (void);
 
diff --git a/gtk/gtkenums.h b/gtk/gtkenums.h
index 468adcdc81..e5ea331172 100644
--- a/gtk/gtkenums.h
+++ b/gtk/gtkenums.h
@@ -1067,4 +1067,64 @@ typedef enum {
   GTK_CONSTRAINT_RELATION_GE = 1
 } GtkConstraintRelation;
 
+/**
+ * GtkConstraintStrength:
+ * @GTK_CONSTRAINT_STRENGTH_REQUIRED: The constraint is required towards solving the layout
+ * @GTK_CONSTRAINT_STRENGTH_STRONG: A strong constraint
+ * @GTK_CONSTRAINT_STRENGTH_MEDIUM: A medium constraint
+ * @GTK_CONSTRAINT_STRENGTH_WEAK: A weak constraint
+ *
+ * The strength of a constraint, expressed as a symbolic constant.
+ *
+ * The strength of a #GtkConstraint can be expressed with any positive
+ * integer; the values of this enumeration can be used for readability.
+ */
+typedef enum {
+  GTK_CONSTRAINT_STRENGTH_REQUIRED = 0,
+  GTK_CONSTRAINT_STRENGTH_STRONG = -1,
+  GTK_CONSTRAINT_STRENGTH_MEDIUM = -2,
+  GTK_CONSTRAINT_STRENGTH_WEAK = -3
+} GtkConstraintStrength;
+
+/**
+ * GtkConstraintAttribute:
+ * @GTK_CONSTRAINT_ATTRIBUTE_NONE: No attribute, used for constant
+ *   relations
+ * @GTK_CONSTRAINT_ATTRIBUTE_LEFT: The left edge of a widget, regardless of
+ *   text direction
+ * @GTK_CONSTRAINT_ATTRIBUTE_RIGHT: The right edge of a widget, regardless
+ *   of text direction
+ * @GTK_CONSTRAINT_ATTRIBUTE_TOP: The top edge of a widget
+ * @GTK_CONSTRAINT_ATTRIBUTE_BOTTOM: The bottom edge of a widget
+ * @GTK_CONSTRAINT_ATTRIBUTE_START: The leading edge of a widget, depending
+ *   on text direction; equivalent to %GTK_CONSTRAINT_ATTRIBUTE_LEFT for LTR
+ *   languages, and %GTK_CONSTRAINT_ATTRIBUTE_RIGHT for RTL ones
+ * @GTK_CONSTRAINT_ATTRIBUTE_END: The trailing edge of a widget, depending
+ *   on text direction; equivalent to %GTK_CONSTRAINT_ATTRIBUTE_RIGHT for LTR
+ *   languages, and %GTK_CONSTRAINT_ATTRIBUTE_LEFT for RTL ones
+ * @GTK_CONSTRAINT_ATTRIBUTE_WIDTH: The width of a widget
+ * @GTK_CONSTRAINT_ATTRIBUTE_HEIGHT: The height of a widget
+ * @GTK_CONSTRAINT_ATTRIBUTE_CENTER_X: The center of a widget, on the
+ *   horizontal axis
+ * @GTK_CONSTRAINT_ATTRIBUTE_CENTER_Y: The center of a widget, on the
+ *   vertical axis
+ * @GTK_CONSTRAINT_ATTRIBUTE_BASELINE: The baseline of a widget
+ *
+ * The widget attributes that can be used when creating a #GtkConstraint.
+ */
+typedef enum {
+  GTK_CONSTRAINT_ATTRIBUTE_NONE,
+  GTK_CONSTRAINT_ATTRIBUTE_LEFT,
+  GTK_CONSTRAINT_ATTRIBUTE_RIGHT,
+  GTK_CONSTRAINT_ATTRIBUTE_TOP,
+  GTK_CONSTRAINT_ATTRIBUTE_BOTTOM,
+  GTK_CONSTRAINT_ATTRIBUTE_START,
+  GTK_CONSTRAINT_ATTRIBUTE_END,
+  GTK_CONSTRAINT_ATTRIBUTE_WIDTH,
+  GTK_CONSTRAINT_ATTRIBUTE_HEIGHT,
+  GTK_CONSTRAINT_ATTRIBUTE_CENTER_X,
+  GTK_CONSTRAINT_ATTRIBUTE_CENTER_Y,
+  GTK_CONSTRAINT_ATTRIBUTE_BASELINE
+} GtkConstraintAttribute;
+
 #endif /* __GTK_ENUMS_H__ */
diff --git a/gtk/meson.build b/gtk/meson.build
index b8cef8bb28..a79e3e7ecc 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -202,6 +202,8 @@ gtk_public_sources = files([
   'gtkcombobox.c',
   'gtkcomboboxtext.c',
   'gtkcomposetable.c',
+  'gtkconstraintlayout.c',
+  'gtkconstraint.c',
   'gtkcontainer.c',
   'gtkcssprovider.c',
   'gtkdialog.c',
@@ -461,6 +463,8 @@ gtk_public_headers = files([
   'gtkcolorutils.h',
   'gtkcombobox.h',
   'gtkcomboboxtext.h',
+  'gtkconstraintlayout.h',
+  'gtkconstraint.h',
   'gtkcontainer.h',
   'gtkcssprovider.h',
   'gtkcustomlayout.h',


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