From 9cfb89990ee1607372b217027dd81738888833ba Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Mon, 25 Mar 2013 17:59:55 +0100 Subject: [PATCH] GtkButtonBox: Support baseline alignment --- gtk/gtkbbox.c | 184 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 161 insertions(+), 23 deletions(-) diff --git a/gtk/gtkbbox.c b/gtk/gtkbbox.c index 8385e2dd2e..638959f740 100644 --- a/gtk/gtkbbox.c +++ b/gtk/gtkbbox.c @@ -101,6 +101,12 @@ static void gtk_button_box_get_preferred_height_for_width (GtkWidget *widget, gint width, gint *minimum, gint *natural); +static void gtk_button_box_get_preferred_height_and_baseline_for_width (GtkWidget *widget, + gint width, + gint *minimum, + gint *natural, + gint *minimum_baseline, + gint *natural_baseline); static void gtk_button_box_size_allocate (GtkWidget *widget, GtkAllocation *allocation); @@ -143,6 +149,7 @@ gtk_button_box_class_init (GtkButtonBoxClass *class) widget_class->get_preferred_height = gtk_button_box_get_preferred_height; widget_class->get_preferred_width_for_height = gtk_button_box_get_preferred_width_for_height; widget_class->get_preferred_height_for_width = gtk_button_box_get_preferred_height_for_width; + widget_class->get_preferred_height_and_baseline_for_width = gtk_button_box_get_preferred_height_and_baseline_for_width; widget_class->size_allocate = gtk_button_box_size_allocate; container_class->remove = gtk_button_box_remove; @@ -438,7 +445,10 @@ gtk_button_box_child_requisition (GtkWidget *widget, gint *nvis_children, gint *nvis_secondaries, gint **widths, - gint **heights) + gint **heights, + gint **baselines, + gint *baseline, + gint *baseline_height) { GtkButtonBox *bbox; GList *children, *list; @@ -446,6 +456,7 @@ gtk_button_box_child_requisition (GtkWidget *widget, gint nsecondaries; gint needed_width; gint needed_height; + gint needed_above, needed_below; gint avg_w, avg_h; GtkRequisition child_requisition; gint ipad_w; @@ -456,11 +467,15 @@ gtk_button_box_child_requisition (GtkWidget *widget, gint ipad_y; gboolean homogeneous; gint i; + gint max_above, max_below, child_baseline; + GtkOrientation orientation; + gboolean have_baseline; g_return_if_fail (GTK_IS_BUTTON_BOX (widget)); bbox = GTK_BUTTON_BOX (widget); + orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget)); homogeneous = gtk_box_get_homogeneous (GTK_BOX (widget)); gtk_widget_style_get (widget, @@ -475,22 +490,33 @@ gtk_button_box_child_requisition (GtkWidget *widget, list = children = _gtk_box_get_children (GTK_BOX (bbox)); needed_width = child_min_width; needed_height = child_min_height; + needed_above = 0; + needed_below = 0; ipad_w = ipad_x * 2; ipad_h = ipad_y * 2; + have_baseline = FALSE; + max_above = max_below = 0; avg_w = avg_h = 0; - while (children) + for (children = list; children != NULL; children = children->next) { GtkWidget *child; child = children->data; - children = children->next; if (gtk_widget_get_visible (child)) { nchildren += 1; - gtk_widget_get_preferred_size (child, - &child_requisition, NULL); + gtk_widget_get_preferred_size_and_baseline (child, + &child_requisition, NULL, &child_baseline, NULL); + if (orientation == GTK_ORIENTATION_HORIZONTAL && + gtk_widget_get_valign_with_baseline (child) == GTK_ALIGN_BASELINE && + child_baseline != -1) + { + have_baseline = TRUE; + max_above = MAX (max_above, child_baseline + ipad_y); + max_below = MAX (max_below , child_requisition.height + ipad_h - (child_baseline + ipad_y)); + } avg_w += child_requisition.width + ipad_w; avg_h += child_requisition.height + ipad_h; } @@ -498,8 +524,14 @@ gtk_button_box_child_requisition (GtkWidget *widget, avg_w /= MAX (nchildren, 1); avg_h /= MAX (nchildren, 1); + if (baseline) + *baseline = have_baseline ? max_above : -1; + if (baseline_height) + *baseline_height = max_above + max_below; + *widths = g_new (gint, nchildren); *heights = g_new (gint, nchildren); + *baselines = g_new (gint, nchildren); i = 0; children = list; @@ -520,7 +552,8 @@ gtk_button_box_child_requisition (GtkWidget *widget, if (is_secondary) nsecondaries++; - gtk_widget_get_preferred_size (child, &child_requisition, NULL); + gtk_widget_get_preferred_size_and_baseline (child, + &child_requisition, NULL, &child_baseline, NULL); if (homogeneous || (!non_homogeneous && (child_requisition.width + ipad_w < avg_w * 1.5))) @@ -534,16 +567,38 @@ gtk_button_box_child_requisition (GtkWidget *widget, (*widths)[i] = child_requisition.width + ipad_w; } + (*baselines)[i] = -1; + if (homogeneous || (!non_homogeneous && (child_requisition.height + ipad_h < avg_h * 1.5))) { (*heights)[i] = -1; - if (child_requisition.height + ipad_h > needed_height) - needed_height = child_requisition.height + ipad_h; + + if (orientation == GTK_ORIENTATION_HORIZONTAL && + gtk_widget_get_valign_with_baseline (child) == GTK_ALIGN_BASELINE && + child_baseline != -1) + { + (*baselines)[i] = child_baseline + ipad_y; + + if (child_baseline + ipad_y > needed_above) + needed_above = child_baseline + ipad_y; + if (child_requisition.height - child_baseline + ipad_y > needed_below) + needed_below = child_requisition.height - child_baseline + ipad_y; + } + else + { + if (child_requisition.height + ipad_h > needed_height) + needed_height = child_requisition.height + ipad_h; + } } else { (*heights)[i] = child_requisition.height + ipad_h; + + if (orientation == GTK_ORIENTATION_HORIZONTAL && + gtk_widget_get_valign_with_baseline (child) == GTK_ALIGN_BASELINE && + child_baseline != -1) + (*baselines)[i] = child_baseline + ipad_y; } i++; @@ -552,12 +607,18 @@ gtk_button_box_child_requisition (GtkWidget *widget, g_list_free (list); + needed_height = MAX (needed_height, needed_above + needed_below); + for (i = 0; i < nchildren; i++) { if ((*widths)[i] == -1) (*widths)[i] = needed_width; if ((*heights)[i] == -1) - (*heights)[i] = needed_height; + { + (*heights)[i] = needed_height; + if ((*baselines)[i] != -1) + (*baselines)[i] = needed_above; + } } if (nvis_children) @@ -569,19 +630,24 @@ gtk_button_box_child_requisition (GtkWidget *widget, static void gtk_button_box_size_request (GtkWidget *widget, - GtkRequisition *requisition) + GtkRequisition *requisition, + gint *baseline) { GtkButtonBoxPrivate *priv; GtkButtonBox *bbox; gint nvis_children; - gint max_size; + gint max_size, max_above, max_below; gint total_size; gint spacing; GtkOrientation orientation; gint *widths; gint *heights; + gint *baselines; gint i; + if (baseline) + *baseline = -1; + bbox = GTK_BUTTON_BOX (widget); priv = bbox->priv; @@ -591,16 +657,22 @@ gtk_button_box_size_request (GtkWidget *widget, gtk_button_box_child_requisition (widget, &nvis_children, NULL, - &widths, &heights); + &widths, &heights, &baselines, baseline, NULL); - max_size = 0; + max_size = max_above = max_below = 0; total_size = 0; for (i = 0; i < nvis_children; i++) { if (orientation == GTK_ORIENTATION_HORIZONTAL) { total_size += widths[i]; - max_size = MAX (max_size, heights[i]); + if (baselines[i] == -1) + max_size = MAX (max_size, heights[i]); + else + { + max_above = MAX (max_above, baselines[i]); + max_below = MAX (max_below, heights[i] - baselines[i]); + } } else { @@ -610,6 +682,23 @@ gtk_button_box_size_request (GtkWidget *widget, } g_free (widths); g_free (heights); + g_free (baselines); + + max_size = MAX (max_size, max_above + max_below); + + switch (gtk_box_get_baseline_position (GTK_BOX (widget))) + { + case GTK_BASELINE_POSITION_TOP: + break; + case GTK_BASELINE_POSITION_CENTER: + if (baseline != NULL && *baseline != -1) + *baseline += (max_size - (max_above + max_below)) / 2; + break; + case GTK_BASELINE_POSITION_BOTTOM: + if (baseline != NULL && *baseline != -1) + *baseline += max_size - (max_above + max_below); + break; + } if (nvis_children == 0) { @@ -656,7 +745,7 @@ gtk_button_box_get_preferred_width (GtkWidget *widget, { GtkRequisition requisition; - gtk_button_box_size_request (widget, &requisition); + gtk_button_box_size_request (widget, &requisition, NULL); *minimum = *natural = requisition.width; } @@ -666,11 +755,9 @@ gtk_button_box_get_preferred_height (GtkWidget *widget, gint *minimum, gint *natural) { - GtkRequisition requisition; - - gtk_button_box_size_request (widget, &requisition); - - *minimum = *natural = requisition.height; + gtk_button_box_get_preferred_height_and_baseline_for_width (widget, -1, + minimum, natural, + NULL, NULL); } static void @@ -691,6 +778,26 @@ gtk_button_box_get_preferred_height_for_width (GtkWidget *widget, gtk_button_box_get_preferred_height (widget, minimum, natural); } +static void +gtk_button_box_get_preferred_height_and_baseline_for_width (GtkWidget *widget, + gint width, + gint *minimum, + gint *natural, + gint *minimum_baseline, + gint *natural_baseline) +{ + GtkRequisition requisition; + gint baseline; + + gtk_button_box_size_request (widget, &requisition, &baseline); + + *minimum = *natural = requisition.height; + if (minimum_baseline) + *minimum_baseline = baseline; + if (natural_baseline) + *natural_baseline = baseline; +} + static void gtk_button_box_size_allocate (GtkWidget *widget, GtkAllocation *allocation) @@ -714,10 +821,13 @@ gtk_button_box_size_allocate (GtkWidget *widget, gint ipad_x, ipad_y; gint *widths; gint *heights; + gint *baselines; gint *sizes; gint primary_size; gint secondary_size; gint total_size; + gint baseline, baseline_height; + gint child_baseline, allocated_baseline; gint i; bbox = GTK_BUTTON_BOX (widget); @@ -733,7 +843,27 @@ gtk_button_box_size_allocate (GtkWidget *widget, gtk_button_box_child_requisition (widget, &nvis_children, &n_secondaries, - &widths, &heights); + &widths, &heights, &baselines, &baseline, &baseline_height); + + allocated_baseline = gtk_widget_get_allocated_baseline (widget); + if (allocated_baseline != -1) + baseline = allocated_baseline; + else if (baseline != -1) + { + /* TODO: modify baseline based on baseline_pos && allocated_baseline*/ + switch (gtk_box_get_baseline_position (GTK_BOX (widget))) + { + case GTK_BASELINE_POSITION_TOP: + baseline = baseline; + break; + case GTK_BASELINE_POSITION_CENTER: + baseline = baseline + (allocation->height - baseline_height) / 2; + break; + case GTK_BASELINE_POSITION_BOTTOM: + baseline = allocation->height - (baseline_height - baseline); + break; + } + } n_primaries = nvis_children - n_secondaries; primary_size = 0; @@ -917,10 +1047,17 @@ gtk_button_box_size_allocate (GtkWidget *widget, { child_allocation.width = widths[i]; child_allocation.height = heights[i]; + child_baseline = -1; if (orientation == GTK_ORIENTATION_HORIZONTAL) { - child_allocation.y = allocation->y + (allocation->height - child_allocation.height) / 2; + if (baselines[i] != -1) + { + child_allocation.y = allocation->y + baseline - baselines[i]; + child_baseline = baselines[i]; + } + else + child_allocation.y = allocation->y + (allocation->height - child_allocation.height) / 2; if (gtk_button_box_get_child_secondary (bbox, child)) { @@ -953,7 +1090,7 @@ gtk_button_box_size_allocate (GtkWidget *widget, } } - gtk_widget_size_allocate (child, &child_allocation); + gtk_widget_size_allocate_with_baseline (child, &child_allocation, child_baseline); i++; } } @@ -961,6 +1098,7 @@ gtk_button_box_size_allocate (GtkWidget *widget, g_list_free (list); g_free (widths); g_free (heights); + g_free (baselines); } /**