diff options
author | Matthias Clasen <mclasen@redhat.com> | 2019-07-02 20:28:11 +0000 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2019-07-02 20:28:11 +0000 |
commit | 4eb28908b81b55df4461486f29584e8213dc7e0f (patch) | |
tree | 8fa7d102db8a40d2ef6928a2a4ba01943c0e4e59 | |
parent | 138195998d1bafe939bd3c627f6ab012842ced39 (diff) | |
download | gtk+-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.c | 479 | ||||
-rw-r--r-- | gtk/gtkgridconstraint.h | 3 |
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 |