[gtk/constraint-grid: 1/3] Add a constraint-based grid



commit 4751f375dc6a37c2fe721fea3d8ffd2a3e1c57c8
Author: Matthias Clasen <mclasen redhat com>
Date:   Tue Jun 25 04:53:39 2019 +0000

    Add a constraint-based grid

 gtk/gtk.h                      |   1 +
 gtk/gtkconstraintlayout.c      | 223 ++++++++++++++++++++++++++++++++++++++++-
 gtk/gtkconstraintlayout.h      |   5 +
 gtk/gtkgridconstraint.c        | 206 +++++++++++++++++++++++++++++++++++++
 gtk/gtkgridconstraint.h        |  49 +++++++++
 gtk/gtkgridconstraintprivate.h |  47 +++++++++
 gtk/meson.build                |   1 +
 7 files changed, 531 insertions(+), 1 deletion(-)
---
diff --git a/gtk/gtk.h b/gtk/gtk.h
index ecf4829339..59f441bd70 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -132,6 +132,7 @@
 #include <gtk/gtkgesturezoom.h>
 #include <gtk/gtkglarea.h>
 #include <gtk/gtkgrid.h>
+#include <gtk/gtkgridconstraint.h>
 #include <gtk/gtkgridlayout.h>
 #include <gtk/gtkheaderbar.h>
 #include <gtk/gtkicontheme.h>
diff --git a/gtk/gtkconstraintlayout.c b/gtk/gtkconstraintlayout.c
index 6cd6a7c70e..0ef21ad4b4 100644
--- a/gtk/gtkconstraintlayout.c
+++ b/gtk/gtkconstraintlayout.c
@@ -62,6 +62,7 @@
 #include "gtkconstraintlayout.h"
 
 #include "gtkconstraintprivate.h"
+#include "gtkgridconstraintprivate.h"
 #include "gtkconstraintexpressionprivate.h"
 #include "gtkconstraintsolverprivate.h"
 #include "gtklayoutchild.h"
@@ -109,6 +110,8 @@ struct _GtkConstraintLayout
    * parent widget, using the public API objects.
    */
   GHashTable *constraints;
+
+  GHashTable *grid_constraints;
 };
 
 G_DEFINE_TYPE (GtkConstraintLayoutChild, gtk_constraint_layout_child, GTK_TYPE_LAYOUT_CHILD)
@@ -381,6 +384,7 @@ gtk_constraint_layout_finalize (GObject *gobject)
 
   g_clear_pointer (&self->bound_attributes, g_hash_table_unref);
   g_clear_pointer (&self->constraints, g_hash_table_unref);
+  g_clear_pointer (&self->grid_constraints, g_hash_table_unref);
 
   G_OBJECT_CLASS (gtk_constraint_layout_parent_class)->finalize (gobject);
 }
@@ -986,6 +990,9 @@ gtk_constraint_layout_allocate (GtkLayoutManager *manager,
   gtk_constraint_solver_remove_constraint (solver, stay_l);
 }
 
+static void layout_add_grid_constraint (GtkConstraintLayout *manager,
+                                        GtkGridConstraint   *constraint);
+
 static void
 gtk_constraint_layout_root (GtkLayoutManager *manager)
 {
@@ -1005,9 +1012,15 @@ gtk_constraint_layout_root (GtkLayoutManager *manager)
   while (g_hash_table_iter_next (&iter, &key, NULL))
     {
       GtkConstraint *constraint = key;
-
       layout_add_constraint (self, constraint);
     }
+
+  g_hash_table_iter_init (&iter, self->grid_constraints);
+  while (g_hash_table_iter_next (&iter, &key, NULL))
+    {
+      GtkGridConstraint *constraint = key;
+      layout_add_grid_constraint (self, constraint);
+    }
 }
 
 static void
@@ -1029,6 +1042,14 @@ gtk_constraint_layout_unroot (GtkLayoutManager *manager)
       gtk_constraint_detach (constraint);
     }
 
+  g_hash_table_iter_init (&iter, self->grid_constraints);
+  while (g_hash_table_iter_next (&iter, &key, NULL))
+    {
+      GtkGridConstraint *constraint = key;
+
+      gtk_grid_constraint_detach (constraint);
+    }
+
   self->solver = NULL;
 }
 
@@ -1061,6 +1082,10 @@ gtk_constraint_layout_init (GtkConstraintLayout *self)
     g_hash_table_new_full (NULL, NULL,
                            (GDestroyNotify) g_object_unref,
                            NULL);
+  self->grid_constraints =
+    g_hash_table_new_full (NULL, NULL,
+                           (GDestroyNotify) g_object_unref,
+                           NULL);
 }
 
 /**
@@ -1106,3 +1131,199 @@ gtk_constraint_layout_add_constraint (GtkConstraintLayout *manager,
 
   g_hash_table_add (manager->constraints, constraint);
 }
+
+void
+gtk_constraint_layout_add_grid_constraint (GtkConstraintLayout *manager,
+                                           GtkGridConstraint   *constraint)
+{
+  g_return_if_fail (GTK_IS_CONSTRAINT_LAYOUT (manager));
+  g_return_if_fail (GTK_IS_GRID_CONSTRAINT (constraint));
+  g_return_if_fail (!gtk_grid_constraint_is_attached (constraint));
+
+  layout_add_grid_constraint (manager, constraint);
+
+  g_hash_table_add (manager->grid_constraints, constraint);
+}
+
+static GtkConstraintVariable **
+allocate_variables (GtkConstraintSolver *solver,
+                    const char          *name,
+                    int                  n)
+{
+  GtkConstraintVariable **vars;
+  int i;
+
+  vars = g_new (GtkConstraintVariable *, n);
+  for (i = 0; i < n; i++)
+    {
+      char *vname = g_strdup_printf ("%s%d", name, i);
+      vars[i] = gtk_constraint_solver_create_variable (solver, NULL, vname, 0.0);
+    }
+
+  return vars;
+}
+
+static void
+add_ordering_constraints (GtkConstraintSolver    *solver,
+                          GtkConstraintVariable **vars,
+                          int                     n_vars,
+                          GPtrArray              *refs)
+{
+  int i;
+
+  for (i = 1; i < n_vars; i++)
+    {
+      GtkConstraintRef *ref;
+
+      ref = gtk_constraint_solver_add_constraint (solver,
+                                                  vars[i],
+                                                  GTK_CONSTRAINT_RELATION_GE,
+                                                  gtk_constraint_expression_new_from_variable (vars[i - 1]),
+                                                  GTK_CONSTRAINT_WEIGHT_REQUIRED);
+      g_ptr_array_add (refs, ref);      
+    }
+}
+
+static void
+add_homogeneous_constraints (GtkConstraintSolver    *solver,
+                             GtkConstraintVariable **vars,
+                             int                     n_vars,
+                             GPtrArray              *refs)
+{
+  int i;
+
+  for (i = 2; i < n_vars; i++)
+    {
+      GtkConstraintExpressionBuilder builder;
+      GtkConstraintRef *ref;
+
+      gtk_constraint_expression_builder_init (&builder, solver);
+      gtk_constraint_expression_builder_term (&builder, vars[i]);
+      gtk_constraint_expression_builder_plus (&builder);
+      gtk_constraint_expression_builder_term (&builder, vars[i - 2]);
+      gtk_constraint_expression_builder_divide_by (&builder);
+      gtk_constraint_expression_builder_constant (&builder, 2.0);
+
+      ref = gtk_constraint_solver_add_constraint (solver,
+                                                  vars[i - 1],
+                                                  GTK_CONSTRAINT_RELATION_EQ,
+                                                  gtk_constraint_expression_builder_finish (&builder),
+                                                  GTK_CONSTRAINT_WEIGHT_REQUIRED);
+      g_ptr_array_add (refs, ref);      
+    }
+}
+
+static void
+add_child_constraints (GtkConstraintLayout     *manager,
+                       GtkConstraintSolver     *solver,
+                       GtkGridConstraintChild  *child,
+                       GtkConstraintVariable  **rows,
+                       GtkConstraintVariable  **cols,
+                       GPtrArray               *refs)
+{
+  GtkConstraintLayoutChild *info;
+  GtkConstraintVariable *var;
+  GtkConstraintVariable *var1;
+  GtkConstraintRef *ref;
+
+  info = GTK_CONSTRAINT_LAYOUT_CHILD (gtk_layout_manager_get_layout_child (GTK_LAYOUT_MANAGER (manager), 
child->child));
+
+  var = get_child_attribute (info, solver, child->child, GTK_CONSTRAINT_ATTRIBUTE_LEFT);
+  var1 = cols[child->left];
+
+  ref = gtk_constraint_solver_add_constraint (solver,
+                                              var,
+                                              GTK_CONSTRAINT_RELATION_EQ,
+                                              gtk_constraint_expression_new_from_variable (var1),
+                                              GTK_CONSTRAINT_WEIGHT_REQUIRED);
+  g_ptr_array_add (refs, ref);
+
+  var = get_child_attribute (info, solver, child->child, GTK_CONSTRAINT_ATTRIBUTE_RIGHT);
+  var1 = cols[child->right];
+
+  ref = gtk_constraint_solver_add_constraint (solver,
+                                              var,
+                                              GTK_CONSTRAINT_RELATION_EQ,
+                                              gtk_constraint_expression_new_from_variable (var1),
+                                              GTK_CONSTRAINT_WEIGHT_REQUIRED);
+  g_ptr_array_add (refs, ref);
+
+  var = get_child_attribute (info, solver, child->child, GTK_CONSTRAINT_ATTRIBUTE_TOP);
+  var1 = rows[child->top];
+
+  ref = gtk_constraint_solver_add_constraint (solver,
+                                              var,
+                                              GTK_CONSTRAINT_RELATION_EQ,
+                                              gtk_constraint_expression_new_from_variable (var1),
+                                              GTK_CONSTRAINT_WEIGHT_REQUIRED);
+  g_ptr_array_add (refs, ref);
+
+  var = get_child_attribute (info, solver, child->child, GTK_CONSTRAINT_ATTRIBUTE_BOTTOM);
+  var1 = rows[child->bottom];
+
+  ref = gtk_constraint_solver_add_constraint (solver,
+                                              var,
+                                              GTK_CONSTRAINT_RELATION_EQ,
+                                              gtk_constraint_expression_new_from_variable (var1),
+                                              GTK_CONSTRAINT_WEIGHT_REQUIRED);
+  g_ptr_array_add (refs, ref);
+}
+
+static void
+layout_add_grid_constraint (GtkConstraintLayout *manager,
+                            GtkGridConstraint   *constraint)
+{
+  GtkWidget *layout_widget;
+  GtkConstraintSolver *solver;
+  GtkConstraintVariable **rows;
+  GtkConstraintVariable **cols;
+  int n_rows, n_cols;
+  GPtrArray *refs;
+  int i;
+
+  if (gtk_grid_constraint_is_attached (constraint))
+    return;
+
+  layout_widget = gtk_layout_manager_get_widget (GTK_LAYOUT_MANAGER (manager));
+  if (layout_widget == NULL)
+    return;
+
+  solver = gtk_constraint_layout_get_solver (manager);
+  if (solver == NULL)
+    return;
+
+  refs = g_ptr_array_new ();
+
+  n_rows = n_cols = 0;
+  for (i = 0; i < constraint->children->len; i++)
+    {
+      GtkGridConstraintChild *child = g_ptr_array_index (constraint->children, i);
+      n_rows = MAX (n_rows, child->bottom);
+      n_cols = MAX (n_cols, child->right);
+    }
+  n_rows++;
+  n_cols++;
+
+  rows = allocate_variables (solver, "row", n_rows);
+  cols = allocate_variables (solver, "col", n_cols);
+
+  add_ordering_constraints (solver, rows, n_rows, refs);
+  add_ordering_constraints (solver, cols, n_cols, refs);
+
+  if (constraint->row_homogeneous)
+    add_homogeneous_constraints (solver, rows, n_rows, refs);
+  if (constraint->column_homogeneous)
+    add_homogeneous_constraints (solver, cols, n_cols, refs);
+
+  for (i = 0; i < constraint->children->len; i++)
+    {
+      GtkGridConstraintChild *child = g_ptr_array_index (constraint->children, i);
+      add_child_constraints (manager, solver, child, rows, cols, refs);
+    }
+
+  gtk_grid_constraint_attach (constraint, solver, refs);
+
+  g_free (rows);
+  g_free (cols);
+  g_ptr_array_unref (refs);
+}
diff --git a/gtk/gtkconstraintlayout.h b/gtk/gtkconstraintlayout.h
index bd6528da3b..8bcfb3b93b 100644
--- a/gtk/gtkconstraintlayout.h
+++ b/gtk/gtkconstraintlayout.h
@@ -20,6 +20,7 @@
 
 #include <gtk/gtklayoutmanager.h>
 #include <gtk/gtkconstraint.h>
+#include <gtk/gtkgridconstraint.h>
 
 G_BEGIN_DECLS
 
@@ -42,6 +43,10 @@ GDK_AVAILABLE_IN_ALL
 void                    gtk_constraint_layout_add_constraint    (GtkConstraintLayout *manager,
                                                                  GtkConstraint       *constraint);
 
+GDK_AVAILABLE_IN_ALL
+void                    gtk_constraint_layout_add_grid_constraint (GtkConstraintLayout *manager,
+                                                                   GtkGridConstraint   *constraint);
+
 /**
  * GtkConstraintLayoutChild:
  *
diff --git a/gtk/gtkgridconstraint.c b/gtk/gtkgridconstraint.c
new file mode 100644
index 0000000000..fe22a686fc
--- /dev/null
+++ b/gtk/gtkgridconstraint.c
@@ -0,0 +1,206 @@
+/* gtkgridconstraint.c: Make a grid with constraints
+ * Copyright 2019  Red Hat, inc.
+ *
+ * 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: Matthias Clasen
+ */
+
+#include "config.h"
+
+#include "gtkgridconstraint.h"
+#include "gtkgridconstraintprivate.h"
+
+#include "gtkintl.h"
+#include "gtktypebuiltins.h"
+
+enum {
+  PROP_ROW_HOMOGENEOUS = 1,
+  PROP_COLUMN_HOMOGENEOUS,
+  N_PROPERTIES
+};
+
+static GParamSpec *obj_props[N_PROPERTIES];
+
+G_DEFINE_TYPE (GtkGridConstraint, gtk_grid_constraint, G_TYPE_OBJECT)
+
+static void
+gtk_constraint_set_property (GObject      *gobject,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+  GtkGridConstraint *self = GTK_GRID_CONSTRAINT (gobject);
+
+  switch (prop_id)
+    {
+    case PROP_ROW_HOMOGENEOUS:
+      self->row_homogeneous = g_value_get_boolean (value);
+      break;
+
+    case PROP_COLUMN_HOMOGENEOUS:
+      self->column_homogeneous = g_value_get_boolean (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)
+{
+  GtkGridConstraint *self = GTK_GRID_CONSTRAINT (gobject);
+
+  switch (prop_id)
+    {
+    case PROP_ROW_HOMOGENEOUS:
+      g_value_set_boolean (value, self->row_homogeneous);
+      break;
+
+    case PROP_COLUMN_HOMOGENEOUS:
+      g_value_set_boolean (value, self->column_homogeneous);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gtk_constraint_finalize (GObject *gobject)
+{
+  GtkGridConstraint *self = GTK_GRID_CONSTRAINT (gobject);
+
+  gtk_grid_constraint_detach (self);
+
+  g_ptr_array_free (self->children, TRUE);
+
+  G_OBJECT_CLASS (gtk_grid_constraint_parent_class)->finalize (gobject);
+}
+
+static void
+gtk_grid_constraint_class_init (GtkGridConstraintClass *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;
+
+  /**
+   * GtkGridConstraint:row-homogeneous:
+   *
+   * Whether to make all rows the same height.
+   */
+  obj_props[PROP_ROW_HOMOGENEOUS] =
+    g_param_spec_boolean ("row-homogeneous",
+                          P_("Row homogeneous"),
+                          P_("Row homogeneous"),
+                          FALSE,
+                          G_PARAM_READWRITE |
+                          G_PARAM_STATIC_STRINGS);
+
+  /**
+   * GtkGridConstraint:column-homogeneous:
+   *
+   * Whether to make all columns the same width.
+   */
+  obj_props[PROP_COLUMN_HOMOGENEOUS] =
+    g_param_spec_boolean ("column-homogeneous",
+                          P_("Column homogeneous"),
+                          P_("Column homogeneous"),
+                          FALSE,
+                          G_PARAM_READWRITE |
+                          G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (gobject_class, N_PROPERTIES, obj_props);
+}
+
+static void
+gtk_grid_constraint_init (GtkGridConstraint *self)
+{
+   self->children = g_ptr_array_new_with_free_func (g_free);
+}
+
+GtkGridConstraint *
+gtk_grid_constraint_new (void)
+{
+  return g_object_new (GTK_TYPE_GRID_CONSTRAINT, NULL);
+}
+
+void
+gtk_grid_constraint_add (GtkGridConstraint *self,
+                         GtkWidget         *child,
+                         int                left,
+                         int                right,
+                         int                top,
+                         int                bottom)
+{
+  GtkGridConstraintChild *data;
+
+  g_return_if_fail (GTK_IS_GRID_CONSTRAINT (self));
+  g_return_if_fail (GTK_IS_WIDGET (child));
+  g_return_if_fail (left < right);
+  g_return_if_fail (top < bottom);
+  g_return_if_fail (self->refs == NULL);
+
+  data = g_new0 (GtkGridConstraintChild, 1);
+
+  data->child = child;
+  data->left = left;
+  data->right = right;
+  data->top = top;
+  data->bottom = bottom;
+
+  g_ptr_array_add (self->children, data);
+}
+
+gboolean
+gtk_grid_constraint_is_attached (GtkGridConstraint *self)
+{
+  return self->refs != NULL;
+}
+
+void
+gtk_grid_constraint_attach (GtkGridConstraint   *self,
+                            GtkConstraintSolver *solver,
+                            GPtrArray           *refs)
+{
+  g_return_if_fail (self->refs == NULL);
+
+  self->solver = solver;
+  self->refs = g_ptr_array_ref (refs);
+}
+
+void gtk_grid_constraint_detach (GtkGridConstraint *self)
+{
+  int i;
+
+  if (self->refs == NULL)
+    return;
+
+  for (i = 0; i < self->refs->len; i++)
+    {
+      GtkConstraintRef *ref = g_ptr_array_index (self->refs, i);
+      gtk_constraint_solver_remove_constraint (self->solver, ref);
+    }
+
+  g_clear_pointer (&self->refs, g_ptr_array_unref);
+}
diff --git a/gtk/gtkgridconstraint.h b/gtk/gtkgridconstraint.h
new file mode 100644
index 0000000000..fb35f52057
--- /dev/null
+++ b/gtk/gtkgridconstraint.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2019 Red Hat, Inc.
+ *
+ * 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/>.
+ */
+
+#ifndef __GTK_GRID_CONSTRAINT_H__
+#define __GTK_GRID_CONSTRAINT_H__
+
+#include <gtk/gtkwidget.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_GRID_CONSTRAINT (gtk_grid_constraint_get_type ())
+
+/**
+ * GtkGridConstraint:
+ *
+ * An object used for managing constraints for children in
+ * a constraints layout that are to be arranged in a grid.
+ */
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GtkGridConstraint, gtk_grid_constraint, GTK, GRID_CONSTRAINT, GObject)
+
+GDK_AVAILABLE_IN_ALL
+GtkGridConstraint * gtk_grid_constraint_new     (void);
+
+GDK_AVAILABLE_IN_ALL
+void                gtk_grid_constraint_add     (GtkGridConstraint   *self,
+                                                 GtkWidget           *child,
+                                                 int                  left,
+                                                 int                  right,
+                                                 int                  top,
+                                                 int                  bottom);
+
+G_END_DECLS
+
+#endif  /* __GTK_GRID_CONSTRAINT_H__ */
diff --git a/gtk/gtkgridconstraintprivate.h b/gtk/gtkgridconstraintprivate.h
new file mode 100644
index 0000000000..6ddd85bb0e
--- /dev/null
+++ b/gtk/gtkgridconstraintprivate.h
@@ -0,0 +1,47 @@
+/* 
+ * Copyright 2019  Red Hat, inc.
+ *
+ * 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: Matthias Clasen
+ */
+
+#include "gtkgridconstraint.h"
+#include "gtkconstraintsolverprivate.h"
+
+typedef struct {
+  GtkWidget *child;
+  int left;
+  int right;
+  int top;
+  int bottom;
+} GtkGridConstraintChild;
+
+struct _GtkGridConstraint {
+  GObject parent;
+
+  gboolean row_homogeneous;
+  gboolean column_homogeneous;
+
+  GPtrArray *children;
+
+  GtkConstraintSolver *solver;
+  GPtrArray           *refs;
+};
+
+gboolean gtk_grid_constraint_is_attached (GtkGridConstraint *constraint);
+void gtk_grid_constraint_attach (GtkGridConstraint   *constraint,
+                                 GtkConstraintSolver *solver,
+                                 GPtrArray           *refs);
+void gtk_grid_constraint_detach (GtkGridConstraint *constraint);
diff --git a/gtk/meson.build b/gtk/meson.build
index 1aaf2ae2d0..d57e35e440 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -251,6 +251,7 @@ gtk_public_sources = files([
   'gtkgesturezoom.c',
   'gtkglarea.c',
   'gtkgrid.c',
+  'gtkgridconstraint.c',
   'gtkgridlayout.c',
   'gtkheaderbar.c',
   'gtkicontheme.c',


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