summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2019-06-25 04:53:39 +0000
committerMatthias Clasen <mclasen@redhat.com>2019-06-27 17:04:41 +0000
commit5e8d5dafc195c406375eba8e22dbd751623e4ccb (patch)
treed37756aec8165cab21a14e604ef8ef0896abe4bd
parent7afac642259dc8bf05a53c1674596107ed9d0f83 (diff)
downloadgtk+-5e8d5dafc195c406375eba8e22dbd751623e4ccb.tar.gz
Add a constraint-based grid
This is implemented as a auxiliary object that generates a bunch of constraints (and variables) when added to a constraint layout. Maybe this could be generalized to a 'constraint set' concept, if we come up with other layouts that we want to reimplement in this way.
-rw-r--r--gtk/gtk.h1
-rw-r--r--gtk/gtkconstraintlayout.c233
-rw-r--r--gtk/gtkconstraintlayout.h5
-rw-r--r--gtk/gtkgridconstraint.c206
-rw-r--r--gtk/gtkgridconstraint.h49
-rw-r--r--gtk/gtkgridconstraintprivate.h47
-rw-r--r--gtk/meson.build1
7 files changed, 541 insertions, 1 deletions
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 bbdd27746c..f8df73f3ca 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"
@@ -105,6 +106,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)
@@ -361,6 +364,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);
}
@@ -977,6 +981,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)
{
@@ -996,9 +1003,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
@@ -1020,6 +1033,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;
}
@@ -1052,6 +1073,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);
}
/**
@@ -1121,3 +1146,209 @@ gtk_constraint_layout_remove_constraint (GtkConstraintLayout *manager,
gtk_layout_manager_layout_changed (GTK_LAYOUT_MANAGER (manager));
}
+
+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;
+}
+
+#if 0
+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_MEDIUM);
+ g_ptr_array_add (refs, ref);
+ }
+}
+#endif
+
+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;
+
+ gtk_constraint_solver_freeze (solver);
+
+ 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);
+
+#if 0
+ //FIXME for some reason, these 'obvious' constraints
+ // make things unstable (and they are not really needed)
+ add_ordering_constraints (solver, rows, n_rows, refs);
+ add_ordering_constraints (solver, cols, n_cols, refs);
+#endif
+
+ 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);
+
+ gtk_constraint_solver_thaw (solver);
+}
diff --git a/gtk/gtkconstraintlayout.h b/gtk/gtkconstraintlayout.h
index b5eba7b125..9464f3bf51 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
@@ -45,6 +46,10 @@ GDK_AVAILABLE_IN_ALL
void gtk_constraint_layout_remove_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 a79e3e7ecc..e0fc1f29e9 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -252,6 +252,7 @@ gtk_public_sources = files([
'gtkgesturezoom.c',
'gtkglarea.c',
'gtkgrid.c',
+ 'gtkgridconstraint.c',
'gtkgridlayout.c',
'gtkheaderbar.c',
'gtkicontheme.c',