Container secrets: size allocation, part 6

Baselines

We are entering another of the more mysterious areas of GTK+ size allocation. Baselines move widgets from a simple box-with-width-and-height model to one where widgets can be aligned vertically in more interesting ways. The main place where this is matters is text. The readers eye is very sensitive to words moving up and down as you move along a line of text. Baselines are there to avoid that.

 

Since this is about aligning children vertically wrt. to each other, baselines are only relevant when the container is in horizontal orientation.

Measure above and below

Since children can now have a ‘forced’ alignment, simply t aking the maximum of the children’s heights is no longer sufficient. The alignment might cause children to ‘stick out’ at the top or the bottom, requiring a greater overall height. In order to handle this, we measure the parts ‘above the baseline’ and the parts ‘below the baseline’ separately, and maximize them separately.

for (i = 0; i < 3; i++) {
  gtk_widget_measure (child[i],
                      orientation,
                      sizes[i].minimum_size,
                      &child_min, &child_nat,
                      &child_min_baseline, &child_nat_baseline);

   below_min = MAX (below_min, child_min - child_min_baseline);
   above_min = MAX (above_min, child_min_baseline);
   below_nat = MAX (below_nat, child_nat - child_nat_baseline);
   above_nat = MAX (above_nat, child_nat_baseline);
}

total_min = above_min + below_min;
total_nat = above_nat + below_nat;

This code leaves out some details, such as dealing with children that don’t return a baseline.

Allocate on baseline

On the allocation side, there are two cases: either we are given a baseline that we have to align our children to, or we have to determine a baseline ourselves. In the latter case, we need to do essentially the same we already did for measuring: determine the below and above sizes separately, and use them to find our baseline:

for (i = 0; i < 3; i++) {
  if (gtk_widget_get_valign (child[i]) != GTK_ALIGN_BASELINE)
    continue;

  gtk_widget_measure (child[i],
                      GTK_ORIENTATION_VERTICAL,
                      child_size[i],
                      &child_min, &child_nat,
                      &child_min_baseline, &child_nat_baseline);

  below_min = MAX (below_min, child_min - child_min_baseline);
  below_nat = MAX (below_nat, child_nat - child_nat_baseline);
  above_min = MAX (above_min, child_min_baseline);
  above_nat = MAX (above_nat, child_nat_baseline);
}

When it comes to determining the baseline, we again have a choice to make. When there is more space available than the minimum, do we place the baseline as high as possible, or as low as possible, or somewhere in the middle? GtkBox has a ::baseline-position property to leave this choice to the user, and we do the same here.

switch (baseline_position) {
  case GTK_BASELINE_POSITION_TOP:
    baseline = above_min;
    break;
  case GTK_BASELINE_POSITION_CENTER:
    baseline = above_min + (height - (above_min + below_min)) / 2;
    break;
  case GTK_BASELINE_POSITION_BOTTOM:
    baseline = height - below_min;
    break;
}
Expanded, baseline position: center
Compressed, baseline position: top
Compressed, baseline position: center
Compressed, baseline position: bottom

Summary

This ends our journey through GTK+’s size allocation machinery. I hope you enjoyed it.

References

  1. Container secrets: size allocation
  2. Container secrets: size allocation, part 2
  3. Container secrets: size allocation, part 3
  4. Container secrets: size allocation, part 4
  5. Container secrets: size allocation, part 5
  6. The code with these changes

1 thought on “Container secrets: size allocation, part 6”

Leave a Reply

Your email address will not be published. Required fields are marked *