From 87171469b7dba8ec8ae3397ed292b9d004ef5e90 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 15 Jan 2016 06:47:31 -0500 Subject: [PATCH] box gadget: Implement cross-axis alignment So far, the box gadget is always allocating all children the full size in the cross axis. This behavior corresponds to the align-items: stretch behavior in https://www.w3.org/TR/css-flexbox-1/#align-items-property This commit implements the other modes described there. While widgets have halign/valign properties that we can use for this, the API for inserting gadgets has to change to take an extra align parameter. All callers have been updated to pass GTK_ALIGN_FILL, since that corresponds to the previous behavior. https://bugzilla.gnome.org/show_bug.cgi?id=760668 --- gtk/gtkboxgadget.c | 127 +++++++++++++++++++++++++++++++++++--- gtk/gtkboxgadgetprivate.h | 5 +- gtk/gtknotebook.c | 14 ++--- 3 files changed, 131 insertions(+), 15 deletions(-) diff --git a/gtk/gtkboxgadget.c b/gtk/gtkboxgadget.c index 15fe036262..e02c8e301c 100644 --- a/gtk/gtkboxgadget.c +++ b/gtk/gtkboxgadget.c @@ -45,6 +45,7 @@ typedef struct _GtkBoxGadgetChild GtkBoxGadgetChild; struct _GtkBoxGadgetChild { GObject *object; ComputeExpandFunc compute_expand; + GtkAlign align; }; G_DEFINE_TYPE_WITH_CODE (GtkBoxGadget, gtk_box_gadget, GTK_TYPE_CSS_GADGET, @@ -59,6 +60,26 @@ gtk_box_gadget_child_is_visible (GObject *child) return gtk_css_gadget_get_visible (GTK_CSS_GADGET (child)); } +static GtkAlign +gtk_box_gadget_child_get_align (GtkBoxGadget *gadget, + GtkBoxGadgetChild *child) +{ + GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (GTK_BOX_GADGET (gadget)); + GtkAlign align; + + if (GTK_IS_WIDGET (child->object)) + { + if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) + g_object_get (child->object, "valign", &align, NULL); + else + g_object_get (child->object, "halign", &align, NULL); + } + else + align = child->align; + + return align; +} + static void gtk_box_gadget_measure_child (GObject *child, GtkOrientation orientation, @@ -281,11 +302,69 @@ gtk_box_gadget_allocate (GtkCssGadget *gadget, if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) { gtk_box_gadget_distribute (GTK_BOX_GADGET (gadget), allocation->width, sizes); - for (i = 0 ; i < priv->children->len; i++) + + if (baseline < 0) + { + for (i = 0; i < priv->children->len; i++) + { + GtkBoxGadgetChild *child = &g_array_index (priv->children, GtkBoxGadgetChild, i); + + if (gtk_box_gadget_child_get_align (GTK_BOX_GADGET (gadget), child) == GTK_ALIGN_BASELINE) + { + gint child_min, child_nat; + gint child_baseline_min, child_baseline_nat; + + gtk_box_gadget_measure_child (child->object, + GTK_ORIENTATION_VERTICAL, + sizes[i].minimum_size, + &child_min, &child_nat, + &child_baseline_min, &child_baseline_nat); + baseline = MAX (baseline, child_baseline_min); + } + } + } + + for (i = 0; i < priv->children->len; i++) { GtkBoxGadgetChild *child = &g_array_index (priv->children, GtkBoxGadgetChild, i); + gint child_min, child_nat; + gint child_baseline_min, child_baseline_nat; child_allocation.width = sizes[i].minimum_size; + gtk_box_gadget_measure_child (child->object, + GTK_ORIENTATION_VERTICAL, + child_allocation.width, + &child_min, &child_nat, + &child_baseline_min, &child_baseline_nat); + switch (gtk_box_gadget_child_get_align (GTK_BOX_GADGET (gadget), child)) + { + case GTK_ALIGN_FILL: + child_allocation.height = allocation->height; + child_allocation.y = allocation->y; + break; + case GTK_ALIGN_START: + child_allocation.height = MIN(child_nat, allocation->height); + child_allocation.y = allocation->y; + break; + case GTK_ALIGN_END: + child_allocation.height = MIN(child_nat, allocation->height); + child_allocation.y = allocation->y + allocation->height - child_allocation.height; + break; + case GTK_ALIGN_BASELINE: + if (child_baseline_min >= 0 && baseline >= 0) + { + child_allocation.height = MIN(child_nat, allocation->height); + child_allocation.y = allocation->y + baseline - child_baseline_min; + break; + } + case GTK_ALIGN_CENTER: + child_allocation.height = MIN(child_nat, allocation->height); + child_allocation.y = allocation->y + (allocation->height - child_allocation.height) / 2; + break; + default: + g_assert_not_reached (); + } + gtk_box_gadget_allocate_child (child->object, &child_allocation, baseline, &child_clip); if (i == 0) *out_clip = child_clip; @@ -297,11 +376,42 @@ gtk_box_gadget_allocate (GtkCssGadget *gadget, else { gtk_box_gadget_distribute (GTK_BOX_GADGET (gadget), allocation->height, sizes); + for (i = 0 ; i < priv->children->len; i++) { GtkBoxGadgetChild *child = &g_array_index (priv->children, GtkBoxGadgetChild, i); + gint child_min, child_nat; child_allocation.height = sizes[i].minimum_size; + gtk_box_gadget_measure_child (child->object, + GTK_ORIENTATION_HORIZONTAL, + child_allocation.height, + &child_min, &child_nat, + NULL, NULL); + + switch (gtk_box_gadget_child_get_align (GTK_BOX_GADGET (gadget), child)) + { + case GTK_ALIGN_FILL: + child_allocation.width = allocation->width; + child_allocation.x = allocation->x; + break; + case GTK_ALIGN_START: + child_allocation.width = MIN(child_nat, allocation->width); + child_allocation.x = allocation->x; + break; + case GTK_ALIGN_END: + child_allocation.width = MIN(child_nat, allocation->width); + child_allocation.x = allocation->x + allocation->width - child_allocation.width; + break; + case GTK_ALIGN_BASELINE: + case GTK_ALIGN_CENTER: + child_allocation.width = MIN(child_nat, allocation->width); + child_allocation.x = allocation->x + (allocation->width - child_allocation.width) / 2; + break; + default: + g_assert_not_reached (); + } + gtk_box_gadget_allocate_child (child->object, &child_allocation, -1, &child_clip); if (i == 0) *out_clip = child_clip; @@ -438,13 +548,15 @@ static void gtk_box_gadget_insert_object (GtkBoxGadget *gadget, int pos, GObject *object, - ComputeExpandFunc compute_expand_func) + ComputeExpandFunc compute_expand_func, + GtkAlign align) { GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (gadget); GtkBoxGadgetChild child; child.object = g_object_ref (object); child.compute_expand = compute_expand_func; + child.align = align; if (pos < 0 || pos >= priv->children->len) { @@ -455,7 +567,6 @@ gtk_box_gadget_insert_object (GtkBoxGadget *gadget, } else { - g_array_insert_val (priv->children, pos, child); gtk_css_node_insert_before (gtk_css_gadget_get_node (GTK_CSS_GADGET (gadget)), get_css_node (object), @@ -471,7 +582,8 @@ gtk_box_gadget_insert_widget (GtkBoxGadget *gadget, gtk_box_gadget_insert_object (gadget, pos, G_OBJECT (widget), - (ComputeExpandFunc) gtk_widget_compute_expand); + (ComputeExpandFunc) gtk_widget_compute_expand, + GTK_ALIGN_FILL); } void @@ -520,13 +632,15 @@ gtk_box_gadget_insert_gadget (GtkBoxGadget *gadget, int pos, GtkCssGadget *cssgadget, gboolean hexpand, - gboolean vexpand) + gboolean vexpand, + GtkAlign align) { gtk_box_gadget_insert_object (gadget, pos, G_OBJECT (cssgadget), hexpand ? (vexpand ? (ComputeExpandFunc) gtk_true : only_horizontal) - : (vexpand ? only_vertical : (ComputeExpandFunc) gtk_false)); + : (vexpand ? only_vertical : (ComputeExpandFunc) gtk_false), + align); } void @@ -535,4 +649,3 @@ gtk_box_gadget_remove_gadget (GtkBoxGadget *gadget, { gtk_box_gadget_remove_object (gadget, G_OBJECT (cssgadget)); } - diff --git a/gtk/gtkboxgadgetprivate.h b/gtk/gtkboxgadgetprivate.h index ce64e4186a..8063fccc61 100644 --- a/gtk/gtkboxgadgetprivate.h +++ b/gtk/gtkboxgadgetprivate.h @@ -21,6 +21,7 @@ #define __GTK_BOX_GADGET_PRIVATE_H__ #include "gtk/gtkcssgadgetprivate.h" +#include "gtk/gtkenums.h" G_BEGIN_DECLS @@ -65,10 +66,12 @@ void gtk_box_gadget_insert_gadget (GtkBoxGadget int pos, GtkCssGadget *cssgadget, gboolean hexpand, - gboolean vexpand); + gboolean vexpand, + GtkAlign align); void gtk_box_gadget_remove_gadget (GtkBoxGadget *gadget, GtkCssGadget *cssgadget); + G_END_DECLS #endif /* __GTK_BOX_GADGET_PRIVATE_H__ */ diff --git a/gtk/gtknotebook.c b/gtk/gtknotebook.c index 6f61384520..203e2a89a4 100644 --- a/gtk/gtknotebook.c +++ b/gtk/gtknotebook.c @@ -1312,7 +1312,7 @@ gtk_notebook_init (GtkNotebook *notebook) NULL, NULL); gtk_css_gadget_set_state (priv->stack_gadget, gtk_css_node_get_state (widget_node)); - gtk_box_gadget_insert_gadget (GTK_BOX_GADGET (priv->gadget), -1, priv->stack_gadget, TRUE, TRUE); + gtk_box_gadget_insert_gadget (GTK_BOX_GADGET (priv->gadget), -1, priv->stack_gadget, TRUE, TRUE, GTK_ALIGN_FILL); priv->header_gadget = gtk_box_gadget_new ("header", GTK_WIDGET (notebook), @@ -1321,7 +1321,7 @@ gtk_notebook_init (GtkNotebook *notebook) gtk_css_gadget_add_class (priv->header_gadget, GTK_STYLE_CLASS_TOP); gtk_css_gadget_set_state (priv->header_gadget, gtk_css_node_get_state (widget_node)); gtk_css_gadget_set_visible (priv->header_gadget, FALSE); - gtk_box_gadget_insert_gadget (GTK_BOX_GADGET (priv->gadget), 0, priv->header_gadget, FALSE, FALSE); + gtk_box_gadget_insert_gadget (GTK_BOX_GADGET (priv->gadget), 0, priv->header_gadget, FALSE, FALSE, GTK_ALIGN_FILL); priv->tabs_gadget = gtk_css_custom_gadget_new ("tabs", GTK_WIDGET (notebook), @@ -1333,7 +1333,7 @@ gtk_notebook_init (GtkNotebook *notebook) NULL, NULL); gtk_css_gadget_set_state (priv->tabs_gadget, gtk_css_node_get_state (widget_node)); - gtk_box_gadget_insert_gadget (GTK_BOX_GADGET (priv->header_gadget), 0, priv->tabs_gadget, TRUE, TRUE); + gtk_box_gadget_insert_gadget (GTK_BOX_GADGET (priv->header_gadget), 0, priv->tabs_gadget, TRUE, TRUE, GTK_ALIGN_FILL); } static void @@ -7014,28 +7014,28 @@ gtk_notebook_update_tab_pos (GtkNotebook *notebook) { case GTK_POS_TOP: if (priv->show_tabs) - gtk_box_gadget_insert_gadget (GTK_BOX_GADGET (priv->gadget), 0, priv->header_gadget, FALSE, FALSE); + gtk_box_gadget_insert_gadget (GTK_BOX_GADGET (priv->gadget), 0, priv->header_gadget, FALSE, FALSE, GTK_ALIGN_FILL); gtk_box_gadget_set_orientation (GTK_BOX_GADGET (priv->gadget), GTK_ORIENTATION_VERTICAL); gtk_box_gadget_set_orientation (GTK_BOX_GADGET (priv->header_gadget), GTK_ORIENTATION_HORIZONTAL); break; case GTK_POS_BOTTOM: if (priv->show_tabs) - gtk_box_gadget_insert_gadget (GTK_BOX_GADGET (priv->gadget), 1, priv->header_gadget, FALSE, FALSE); + gtk_box_gadget_insert_gadget (GTK_BOX_GADGET (priv->gadget), 1, priv->header_gadget, FALSE, FALSE, GTK_ALIGN_FILL); gtk_box_gadget_set_orientation (GTK_BOX_GADGET (priv->gadget), GTK_ORIENTATION_VERTICAL); gtk_box_gadget_set_orientation (GTK_BOX_GADGET (priv->header_gadget), GTK_ORIENTATION_HORIZONTAL); break; case GTK_POS_LEFT: if (priv->show_tabs) - gtk_box_gadget_insert_gadget (GTK_BOX_GADGET (priv->gadget), 0, priv->header_gadget, FALSE, FALSE); + gtk_box_gadget_insert_gadget (GTK_BOX_GADGET (priv->gadget), 0, priv->header_gadget, FALSE, FALSE, GTK_ALIGN_FILL); gtk_box_gadget_set_orientation (GTK_BOX_GADGET (priv->gadget), GTK_ORIENTATION_HORIZONTAL); gtk_box_gadget_set_orientation (GTK_BOX_GADGET (priv->header_gadget), GTK_ORIENTATION_VERTICAL); break; case GTK_POS_RIGHT: if (priv->show_tabs) - gtk_box_gadget_insert_gadget (GTK_BOX_GADGET (priv->gadget), 1, priv->header_gadget, FALSE, FALSE); + gtk_box_gadget_insert_gadget (GTK_BOX_GADGET (priv->gadget), 1, priv->header_gadget, FALSE, FALSE, GTK_ALIGN_FILL); gtk_box_gadget_set_orientation (GTK_BOX_GADGET (priv->gadget), GTK_ORIENTATION_HORIZONTAL); gtk_box_gadget_set_orientation (GTK_BOX_GADGET (priv->header_gadget), GTK_ORIENTATION_VERTICAL); break;