summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2019-07-02 20:28:11 +0000
committerMatthias Clasen <mclasen@redhat.com>2019-07-02 20:28:11 +0000
commit4eb28908b81b55df4461486f29584e8213dc7e0f (patch)
tree8fa7d102db8a40d2ef6928a2a4ba01943c0e4e59
parent138195998d1bafe939bd3c627f6ab012842ced39 (diff)
downloadgtk+-constraint-grid-3.tar.gz
Make grid constraint dynamicconstraint-grid-3
Handle dynamic changes: - adding/removing children - changing homogeneity - attaching/detaching from a layout
-rw-r--r--gtk/gtkgridconstraint.c479
-rw-r--r--gtk/gtkgridconstraint.h3
2 files changed, 387 insertions, 95 deletions
diff --git a/gtk/gtkgridconstraint.c b/gtk/gtkgridconstraint.c
index 3b03a18368..b2c02436ee 100644
--- a/gtk/gtkgridconstraint.c
+++ b/gtk/gtkgridconstraint.c
@@ -24,14 +24,58 @@
#include "gtkintl.h"
#include "gtktypebuiltins.h"
+enum {
+ POS_LEFT,
+ POS_RIGHT,
+ POS_TOP,
+ POS_BOTTOM,
+ LAST_POS,
+ SIZE_WIDTH = LAST_POS,
+ SIZE_HEIGHT,
+ LAST_CONSTRAINT
+};
+
+/* We maintain constraints of the form:
+ *
+ * child.top = row_x
+ * child.bottom = row_y
+ *
+ * (and similar for columns). We avoid introducing
+ * extra variables for rows and columns by keeping
+ * track of the first child we encounter that ends
+ * at a given position, and using that instead:
+ *
+ * child.top = first.bottom
+ *
+ * (assuming that @first is the first child we saw
+ * that ends at row x (and has its bottom edge there).
+ *
+ * For homogeneous grids, we additionally maintain
+ * constraints of the form:
+ *
+ * a.width / a.colspan = b.width / b.colspan
+ *
+ * (and similar for heights). We only maintain
+ * these relations between a child and its
+ * predecessor in the list of children.
+ */
+
typedef struct {
GtkConstraintTarget *child;
int left;
int right;
int top;
int bottom;
+
+ /* We hold a ref on these */
+ GtkConstraint *constraints[LAST_CONSTRAINT];
} GtkGridConstraintChild;
+typedef struct {
+ GtkConstraintTarget *target;
+ GtkConstraintAttribute attr;
+} Attach;
+
struct _GtkGridConstraint {
GObject parent;
@@ -40,8 +84,15 @@ struct _GtkGridConstraint {
gboolean row_homogeneous;
gboolean column_homogeneous;
+ /* List<GtkGridConstraintChild>, owned */
GPtrArray *children;
+
+ /* List<GtkConstaint>, not owned */
GPtrArray *constraints;
+
+ /* Array<Attach>, not owning Attach.target */
+ GArray *rows;
+ GArray *cols;
};
enum {
@@ -54,6 +105,11 @@ static GParamSpec *obj_props[N_PROPERTIES];
G_DEFINE_TYPE (GtkGridConstraint, gtk_grid_constraint, G_TYPE_OBJECT)
+static void set_row_homogeneous (GtkGridConstraint *self,
+ gboolean homogeneous);
+static void set_column_homogeneous (GtkGridConstraint *self,
+ gboolean homogeneous);
+
static void
gtk_constraint_set_property (GObject *gobject,
guint prop_id,
@@ -65,11 +121,11 @@ gtk_constraint_set_property (GObject *gobject,
switch (prop_id)
{
case PROP_ROW_HOMOGENEOUS:
- self->row_homogeneous = g_value_get_boolean (value);
+ set_row_homogeneous (self, g_value_get_boolean (value));
break;
case PROP_COLUMN_HOMOGENEOUS:
- self->column_homogeneous = g_value_get_boolean (value);
+ set_column_homogeneous (self, g_value_get_boolean (value));
break;
default:
@@ -103,15 +159,31 @@ gtk_constraint_get_property (GObject *gobject,
}
static void
+gtk_grid_constraint_init (GtkGridConstraint *self)
+{
+ self->children = g_ptr_array_new_with_free_func (g_free);
+
+ self->constraints = g_ptr_array_new ();
+
+ self->rows = g_array_new (FALSE, TRUE, sizeof (Attach));
+ self->cols = g_array_new (FALSE, TRUE, sizeof (Attach));
+}
+
+static void
gtk_constraint_finalize (GObject *gobject)
{
GtkGridConstraint *self = GTK_GRID_CONSTRAINT (gobject);
- gtk_grid_constraint_detach (self);
+ g_array_free (self->rows, TRUE);
+ g_array_free (self->cols, TRUE);
- g_ptr_array_free (self->children, TRUE);
+ if (self->layout)
+ gtk_grid_constraint_detach (self);
+ g_assert (self->constraints->len == 0);
g_ptr_array_free (self->constraints, TRUE);
+ g_ptr_array_free (self->children, TRUE);
+
G_OBJECT_CLASS (gtk_grid_constraint_parent_class)->finalize (gobject);
}
@@ -154,94 +226,265 @@ gtk_grid_constraint_class_init (GtkGridConstraintClass *klass)
}
static void
-gtk_grid_constraint_init (GtkGridConstraint *self)
+remove_child_constraint (GtkGridConstraint *self,
+ GtkGridConstraintChild *child,
+ int pos)
{
- self->children = g_ptr_array_new_with_free_func (g_free);
- self->constraints = g_ptr_array_new ();
+ if (child->constraints[pos] == NULL)
+ return;
+
+ if (self->layout)
+ gtk_constraint_layout_remove_constraint (self->layout,
+ child->constraints[pos]);
+ g_object_unref (child->constraints[pos]);
+ child->constraints[pos] = NULL;
}
-GtkGridConstraint *
-gtk_grid_constraint_new (void)
+/* Ensure that the child variable @var is placed
+ * at the grid edge @pos. If we already have a variable
+ * that needs to end up there, we use it to assert
+ * var = vars[top], otherwise we put @var in the
+ * the list of variables.
+ *
+ * @attr may be one of LEFT/RIGHT/TOP/BOTTOM here.
+ */
+static void
+add_child_constraint (GtkGridConstraint *self,
+ GtkGridConstraintChild *child,
+ GtkConstraintAttribute attr,
+ int pos)
{
- return g_object_new (GTK_TYPE_GRID_CONSTRAINT, NULL);
-}
+ Attach *attach;
+ GArray *vars;
+ int cpos;
+
+ switch ((int)attr)
+ {
+ case GTK_CONSTRAINT_ATTRIBUTE_LEFT:
+ vars = self->cols;
+ cpos = POS_LEFT;
+ break;
+ case GTK_CONSTRAINT_ATTRIBUTE_RIGHT:
+ vars = self->cols;
+ cpos = POS_RIGHT;
+ break;
+ case GTK_CONSTRAINT_ATTRIBUTE_TOP:
+ vars = self->rows;
+ cpos = POS_TOP;
+ break;
+ case GTK_CONSTRAINT_ATTRIBUTE_BOTTOM:
+ vars = self->rows;
+ cpos = POS_BOTTOM;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ if (vars->len <= pos)
+ g_array_set_size (vars, pos + 1);
-/* Ensure that
- * child1.width / child1.colspan == child2.width / child2.colspan
- * (or equivalent for height )
+ attach = &g_array_index (vars, Attach, pos);
+ if (attach->target == NULL)
+ {
+ attach->target = child->child;
+ attach->attr = attr;
+ }
+ else
+ {
+ g_assert (child->constraints[cpos] == NULL);
+
+ child->constraints[cpos] = gtk_constraint_new (child->child, attr,
+ GTK_CONSTRAINT_RELATION_EQ,
+ attach->target, attach->attr,
+ 1.0, 0.0,
+ GTK_CONSTRAINT_STRENGTH_REQUIRED);
+ if (self->layout)
+ gtk_constraint_layout_add_constraint (self->layout,
+ child->constraints[cpos]);
+ }
+}
+
+/* Create the contraint:
+ *
+ * child1.width/child1.colspan = child2.width/child2.colspan
+ *
+ * @attr can be WIDTH or HEIGHT here.
*/
static void
-add_homogeneous_constraint (GtkGridConstraintChild *child1,
+add_homogeneous_constraint (GtkGridConstraint *self,
+ GtkGridConstraintChild *child1,
GtkGridConstraintChild *child2,
- GtkConstraintAttribute attr,
- GPtrArray *constraints)
+ GtkConstraintAttribute attr)
{
int span1, span2;
- GtkConstraint *constraint;
+ int pos;
if (attr == GTK_CONSTRAINT_ATTRIBUTE_WIDTH)
{
+ pos = SIZE_WIDTH;
span1 = child1->right - child1->left;
span2 = child2->right - child2->left;
}
else
{
+ pos = SIZE_HEIGHT;
span1 = child1->bottom - child1->top;
span2 = child2->bottom - child2->top;
}
- constraint = gtk_constraint_new (child1->child, attr,
+ g_assert (child1->constraints[pos] == NULL);
+
+ child1->constraints[pos] = gtk_constraint_new (child1->child, attr,
GTK_CONSTRAINT_RELATION_EQ,
child2->child, attr,
(double) span1 / (double) span2,
0.0,
GTK_CONSTRAINT_STRENGTH_REQUIRED);
- g_ptr_array_add (constraints, constraint);
+
+ if (self->layout)
+ gtk_constraint_layout_add_constraint (self->layout,
+ child1->constraints[pos]);
}
-typedef struct {
- GtkConstraintTarget *target;
- GtkConstraintAttribute attr;
-} Attach;
+static void
+set_row_homogeneous (GtkGridConstraint *self,
+ gboolean homogeneous)
+{
+ GtkGridConstraintChild *child1;
+ GtkGridConstraintChild *child2;
+ int i;
+
+ if (self->row_homogeneous == homogeneous)
+ return;
+
+ self->row_homogeneous = homogeneous;
+
+ for (i = 1; i < self->children->len; i++)
+ {
+ child1 = g_ptr_array_index (self->children, i);
+ child2 = g_ptr_array_index (self->children, i - 1);
+ if (homogeneous)
+ add_homogeneous_constraint (self, child1, child2, GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
+ else
+ remove_child_constraint (self, child1, SIZE_HEIGHT);
+ }
+
+ g_object_notify (G_OBJECT (self), "row-homogeneous");
+}
-/* Ensure that the child variable @var is placed
- * at the grid edge @pos. If we already have a variable
- * that needs to end up there, we use it to assert
- * var = vars[top], otherwise we put @var in the
- * the list of variables.
- */
static void
-add_child_constraint (GtkConstraintTarget *target,
- GtkConstraintAttribute attr,
- int pos,
- GArray *vars,
- GPtrArray *constraints)
+set_column_homogeneous (GtkGridConstraint *self,
+ gboolean homogeneous)
{
- Attach *attach;
+ GtkGridConstraintChild *child1;
+ GtkGridConstraintChild *child2;
+ int i;
- if (vars->len <= pos)
- g_array_set_size (vars, pos + 1);
+ if (self->column_homogeneous == homogeneous)
+ return;
- attach = &g_array_index (vars, Attach, pos);
- if (attach->target == NULL)
+ self->column_homogeneous = homogeneous;
+
+ for (i = 1; i < self->children->len; i++)
{
- attach->target = target;
- attach->attr = attr;
+ child1 = g_ptr_array_index (self->children, i);
+ child2 = g_ptr_array_index (self->children, i - 1);
+ if (homogeneous)
+ add_homogeneous_constraint (self, child1, child2, GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
+ else
+ remove_child_constraint (self, child1, SIZE_WIDTH);
}
+
+ g_object_notify (G_OBJECT (self), "column-homogeneous");
+}
+
+/* Fix up attachment constraints for the removal of @target.
+ * We are fixing up the attachment at row/col @rows and
+ * position @pos. If @target was not the representative
+ * for this position, there is nothing to do (all of @targets
+ * constraints are already removed). Otherwise, look over
+ * all children that are attached to @target for this position,
+ * pick a new representative, and fix up the constraints
+ * for all others to attach to the new representative.
+ */
+static void
+fix_up_attach (GtkGridConstraint *self,
+ GtkConstraintTarget *target,
+ gboolean rows,
+ int pos)
+{
+ int i;
+ Attach *attach;
+
+ if (rows)
+ attach = &g_array_index (self->rows, Attach, pos);
else
- {
- GtkConstraint *constraint;
+ attach = &g_array_index (self->cols, Attach, pos);
- constraint = gtk_constraint_new (target, attr,
- GTK_CONSTRAINT_RELATION_EQ,
- attach->target, attach->attr,
- 1.0, 0.0,
- GTK_CONSTRAINT_STRENGTH_REQUIRED);
- g_ptr_array_add (constraints, constraint);
+ if (attach->target != target)
+ return;
+
+ attach->target = NULL;
+
+ for (i = 0; i < self->children->len; i++)
+ {
+ int cpos;
+ GtkGridConstraintChild *child;
+ GtkConstraintAttribute attr;
+
+ child = g_ptr_array_index (self->children, i);
+ if (rows && child->top == pos)
+ {
+ cpos = POS_TOP;
+ attr = GTK_CONSTRAINT_ATTRIBUTE_TOP;
+ }
+ else if (rows && child->bottom == pos)
+ {
+ cpos = POS_BOTTOM;
+ attr = GTK_CONSTRAINT_ATTRIBUTE_BOTTOM;
+ }
+ else if (!rows && child->left == pos)
+ {
+ cpos = POS_LEFT;
+ attr = GTK_CONSTRAINT_ATTRIBUTE_LEFT;
+ }
+ else if (!rows && child->right == pos)
+ {
+ cpos = POS_LEFT;
+ attr = GTK_CONSTRAINT_ATTRIBUTE_LEFT;
+ }
+ else
+ continue;
+
+ remove_child_constraint (self, child, cpos);
+
+ if (attach->target == NULL)
+ {
+ attach->target = child->child;
+ attach->attr = attr;
+ }
+ else
+ {
+ child->constraints[cpos] =
+ gtk_constraint_new (child->child, attr,
+ GTK_CONSTRAINT_RELATION_EQ,
+ attach->target, attach->attr,
+ 1.0, 0.0,
+ GTK_CONSTRAINT_STRENGTH_REQUIRED);
+ if (self->layout)
+ gtk_constraint_layout_add_constraint (self->layout,
+ g_object_ref (child->constraints[cpos]));
+
+ }
}
}
+GtkGridConstraint *
+gtk_grid_constraint_new (void)
+{
+ return g_object_new (GTK_TYPE_GRID_CONSTRAINT, NULL);
+}
+
void
gtk_grid_constraint_add (GtkGridConstraint *self,
GtkWidget *child,
@@ -250,7 +493,8 @@ gtk_grid_constraint_add (GtkGridConstraint *self,
int top,
int bottom)
{
- GtkGridConstraintChild *data;
+ GtkGridConstraintChild *child1;
+ GtkGridConstraintChild *child2;
g_return_if_fail (GTK_IS_GRID_CONSTRAINT (self));
g_return_if_fail (GTK_IS_WIDGET (child));
@@ -258,88 +502,133 @@ gtk_grid_constraint_add (GtkGridConstraint *self,
g_return_if_fail (top < bottom);
g_return_if_fail (self->layout == NULL);
- data = g_new0 (GtkGridConstraintChild, 1);
+ child1 = g_new0 (GtkGridConstraintChild, 1);
- data->child = GTK_CONSTRAINT_TARGET (child);
- data->left = left;
- data->right = right;
- data->top = top;
- data->bottom = bottom;
+ child1->child = GTK_CONSTRAINT_TARGET (child);
+ child1->left = left;
+ child1->right = right;
+ child1->top = top;
+ child1->bottom = bottom;
- g_ptr_array_add (self->children, data);
-}
+ if (self->children->len > 0)
+ child2 = g_ptr_array_index (self->children, self->children->len - 1);
+ else
+ child2 = NULL;
-gboolean
-gtk_grid_constraint_is_attached (GtkGridConstraint *self)
-{
- return self->layout != NULL;
+ add_child_constraint (self, child1, GTK_CONSTRAINT_ATTRIBUTE_TOP, top);
+ add_child_constraint (self, child1, GTK_CONSTRAINT_ATTRIBUTE_BOTTOM, bottom);
+ add_child_constraint (self, child1, GTK_CONSTRAINT_ATTRIBUTE_LEFT, left);
+ add_child_constraint (self, child1, GTK_CONSTRAINT_ATTRIBUTE_RIGHT, right);
+
+ if (self->row_homogeneous && child2)
+ add_homogeneous_constraint (self, child1, child2, GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
+ if (self->column_homogeneous && child2)
+ add_homogeneous_constraint (self, child1, child2, GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
+
+ g_ptr_array_add (self->children, child1);
}
-static void
-create_constraints (GtkGridConstraint *self)
+void
+gtk_grid_constraint_remove (GtkGridConstraint *self,
+ GtkWidget *child)
{
- GArray *rows;
- GArray *cols;
+ GtkGridConstraintChild *child1 = NULL;
+ GtkGridConstraintChild *next = NULL;
+ GtkGridConstraintChild *prev = NULL;
+ GtkConstraintTarget *target = (GtkConstraintTarget *)child;
int i;
-
- rows = g_array_new (FALSE, TRUE, sizeof (Attach));
- cols = g_array_new (FALSE, TRUE, sizeof (Attach));
+ int left, right, top, bottom;
for (i = 0; i < self->children->len; i++)
{
- GtkGridConstraintChild *child = g_ptr_array_index (self->children, i);
-
- add_child_constraint (child->child, GTK_CONSTRAINT_ATTRIBUTE_TOP, child->top, rows, self->constraints);
- add_child_constraint (child->child, GTK_CONSTRAINT_ATTRIBUTE_BOTTOM, child->bottom, rows, self->constraints);
- add_child_constraint (child->child, GTK_CONSTRAINT_ATTRIBUTE_LEFT, child->left, cols, self->constraints);
- add_child_constraint (child->child, GTK_CONSTRAINT_ATTRIBUTE_RIGHT, child->right, cols, self->constraints);
+ child1 = g_ptr_array_index (self->children, i);
+ if (child1->child == target)
+ break;
+ child1 = NULL;
}
- for (i = 1; i < self->children->len; i++)
+ if (child1 == NULL)
+ return;
+
+ if (i > 0)
+ prev = g_ptr_array_index (self->children, i - 1);
+ if (i + 1 < self->children->len)
+ next = g_ptr_array_index (self->children, i + 1);
+
+ for (i = 0; i < LAST_CONSTRAINT; i++)
+ remove_child_constraint (self, child1, i);
+
+ top = child1->top;
+ bottom = child1->bottom;
+ left = child1->left;
+ right = child1->right;
+
+ g_ptr_array_remove (self->children, child1);
+
+ fix_up_attach (self, target, TRUE, top);
+ fix_up_attach (self, target, TRUE, bottom);
+ fix_up_attach (self, target, FALSE, right);
+ fix_up_attach (self, target, FALSE, left);
+
+ if (self->column_homogeneous && next)
+ {
+ remove_child_constraint (self, next, SIZE_WIDTH);
+ if (prev)
+ add_homogeneous_constraint (self, next, prev, GTK_CONSTRAINT_ATTRIBUTE_WIDTH);
+ }
+ if (self->row_homogeneous && next)
{
- GtkGridConstraintChild *child1 = g_ptr_array_index (self->children, i);
- GtkGridConstraintChild *child2 = g_ptr_array_index (self->children, i - 1);
- if (self->row_homogeneous)
- add_homogeneous_constraint (child1, child2, GTK_CONSTRAINT_ATTRIBUTE_HEIGHT, self->constraints);
- if (self->column_homogeneous)
- add_homogeneous_constraint (child1, child2, GTK_CONSTRAINT_ATTRIBUTE_WIDTH, self->constraints);
+ remove_child_constraint (self, next, SIZE_HEIGHT);
+ if (prev)
+ add_homogeneous_constraint (self, next, prev, GTK_CONSTRAINT_ATTRIBUTE_HEIGHT);
}
+}
- g_array_free (rows, TRUE);
- g_array_free (cols, TRUE);
+gboolean
+gtk_grid_constraint_is_attached (GtkGridConstraint *self)
+{
+ return self->layout != NULL;
}
void
gtk_grid_constraint_attach (GtkGridConstraint *self,
GtkConstraintLayout *layout)
{
- int i;
+ int i, j;
g_return_if_fail (self->layout == NULL);
self->layout = layout;
- create_constraints (self);
-
- for (i = 0; i < self->constraints->len; i++)
+ for (i = 0; i < self->children->len; i++)
{
- GtkConstraint *constraint = g_ptr_array_index (self->constraints, i);
- gtk_constraint_layout_add_constraint (layout, g_object_ref (constraint));
+ GtkGridConstraintChild *child = g_ptr_array_index (self->children, i);
+ for (j = 0; j < LAST_CONSTRAINT; j++)
+ {
+ if (child->constraints[j] != NULL)
+ gtk_constraint_layout_add_constraint (self->layout,
+ g_object_ref (child->constraints[j]));
+ }
}
}
void
gtk_grid_constraint_detach (GtkGridConstraint *self)
{
- int i;
+ int i, j;
if (self->layout == NULL)
return;
- for (i = 0; i < self->constraints->len; i++)
+ for (i = 0; i < self->children->len; i++)
{
- GtkConstraint *constraint = g_ptr_array_index (self->constraints, i);
- gtk_constraint_layout_remove_constraint (self->layout, constraint);
+ GtkGridConstraintChild *child = g_ptr_array_index (self->children, i);
+ for (j = 0; j < LAST_CONSTRAINT; j++)
+ {
+ if (child->constraints[j] != NULL)
+ gtk_constraint_layout_remove_constraint (self->layout,
+ child->constraints[j]);
+ }
}
self->layout = NULL;
diff --git a/gtk/gtkgridconstraint.h b/gtk/gtkgridconstraint.h
index fb885d33f7..8c939e1285 100644
--- a/gtk/gtkgridconstraint.h
+++ b/gtk/gtkgridconstraint.h
@@ -46,6 +46,9 @@ void gtk_grid_constraint_add (GtkGridConstraint *self,
int bottom);
GDK_AVAILABLE_IN_ALL
+void gtk_grid_constraint_remove (GtkGridConstraint *self,
+ GtkWidget *child);
+GDK_AVAILABLE_IN_ALL
void gtk_grid_constraint_attach (GtkGridConstraint *self,
GtkConstraintLayout *layout);
GDK_AVAILABLE_IN_ALL