Re: Baseline alignment ideas



On Tue, 2013-02-26 at 15:30 +0100, Alexander Larsson wrote:
One of the main layout features we're badly missing atm is baseline
alignment. I spent some time looking at this recently but I bumped
into some problems. I'd like to bring this up for discussion in the
hope that we might figure out a solution to my problems.

First, let me define what i mean by baseline layout and the API I
expect it to have. Every widget has a single baseline, which is
an offset from the top of the allocation to the baseline.
Some layouts (such as e.g. multi-line text) have multiple possible
baselines, but this is outside of the common gtk+ api and each
widget gets to decide which one to use, via some per-widget API.

We add a new GtkAlign element, GTK_ALIGN_BASELINE, which when not
supported does the same as CENTER, but in containers that support
baseline alignment we align all the children with BASELINE up so
that they all have the baseline at the same y coord. Additionally
we need a single setting for the container itself that decides how
to align the whole group of baseline aligned widgets (i.e. do we
put the baseline alignment widgets at the bottom or at the top?

An example hbox layout could look like with baseline at botttom:

+-----------------------------------------------+
|+-------+                                      |
|| START |                                      |
|+-------+                                      |
|                                     +--------+|
|                   +-------+         | CENTER ||
|          +-------+|       |         +--------+|
|          |--BASE-||-BASE--|+-------+          |
|          |       |+-------+|  END  |          |
|          +-------+         +-------+          |
+-----------------------------------------------+

Now, back to the problem. Consider implementing size request/allocation
for the container above. We can start with the min width, asking each
child for min width and summing that. Then we ask for min height for
that width and for the baseline at that width. We can thus simply
calculate the min and natural size requests and their corresponding
baseline.

One thing to keep in mind here, please avoid assumptions of
minimum widths (when doing h4w, or minimum heights when
doing w4h).

The height-for-minimum-width is invariably the tallest height
that a widget can request, so guess work like this really doesn't
work (as you have recognized in the following paragraph).

The problem comes later, when we get the final size allocation for the
container. Its easy to distribute the widths, but if the final height
is not the same as the min/natural request, how do we distribute the
height among the baseline aligned children such that the total height
after we baseline align them fits in the assigned height?

Since the baseline position depends (non-linearly) on the exact height
we assign to the child, and the total size depends on the baselines
this seems circular, and I can't see any way to calculate this other
than in some iterative fashion.

My thoughts are that, when performing get_preferred_height_for_width(),
you need some virtual allocation logic like such that is found in
gtkbox.c (when we are requesting in the 'uncomfortable/opposing'
orientation):

  a.) Calculate how much width exactly would be given to each child
  b.) Calculate height-for-width of each child
  c.) Report the MAX of all child heights.

Now with baselines, this becomes slightly more complex, during
the h4w request phase you need to replace the above 'c' with
something that aligns the requested baseline(s) for all children,
and requests a little bit more height in order to logically fit
all children into that height (which is dependent on the provided
width).

For instance, if you have two children with the baseline at the top,
then all extra space can be assigned to each child. However, if one
has the baseline att the bottom you need to give each half the extra
height.

I don't really see any way to solve this generically. But maybe we can
somehow limit our baseline support so that this works? For instance,
we could always request the naural size for baseline aligned widgets
and never grow them? Would that be enough in practical use? I dunno...

Ideas wanted...

I had some ideas about this, and I'm sure it can be done with
height-for-width in mind, however the "y" baseline(s) need to be
calculated on a per width basis (i.e. as you're problem statement
indicates, one "y" baseline can not rule-them-all as the required "y"
anchor point might differ depending on the width, so probably the
size requesting cache needs to also cache any baselines reported).

I don't think something particularly iterative is needed for this
either, just need to virtually allocate the children in the request
phase.

Also, I'm not convinced that 'baseline' is the right word for this API,
I would rather consider it in terms of X and Y "anchor points" (if we
handle this complexity in one orientation, we should be able to get
the other orientation with relatively little added effort).

Another thing, is that we need the ability to calculate more than
a single anchor point in a given orientation per widget. The
one 'baseline' per widget wont pan out in the long run.

There are two things to consider:

  a.) A single content may have multiple base lines, the
      amount of baselines reported by some content might depend
      on the allocated width, even (consider a wrapping multi-line
      label or a GtkWrapBox with a variable amount of
      'rows-of-widgets').

      The question about GtkLabel text baselines might be possible
      to work around, for instance if consistent line height
      is enforced in wrapping labels (but IIRC Pango has
      apis which can report the baselines of each line in a
      PangoLayout with an allocated 'width').

  b.) Baseline logic should not be constrained to a single
      container's children (although through recursion
      into children, the idea that a widget/container has
      multiple baselines might render this detail transparent),
      for instance:

+------------------------------------------+
| Horizontal Box (or Grid)                 |
|                                          |
|                +-----------------------+ |
| +---------+    | Vertical Box (or Grid)| |
| | Image   |    |   +--------+          | |
| |         |    |   | Label  |          | |
| +---------+~~~~|~~~+--------+          | |
|                |                       | |
|                |   +---------------+   | |
|                |   | Other Content |   | |
|                |   +---------------+   | |
|                +-----------------------+ |
+------------------------------------------+

      So... in other words the Vertical Box element here must
      report possibly a number of "y anchors" for the anchors
      of it's children, and use the allocated anchors from it's
      parent to properly position the "Label" content it contains.

I haven't taken the time to consider solutions to all of these
problems, but I'm convinced they can be solved (and without being
overly iterative in the process).

Another question to fiddle with while pondering anchor points
and baselines, is to what extent can this API be implicit, and
to what extent should it be explicit. GtkLabels could have the
capacity to report a number of baselines (Y anchors) for a
hypothetical allocation width (by using the Pango apis to report
them). This would be the implicit part.

One might think though, an application developer might want to 
add symbolic anchor points to their widgets. for instance in
the above ascii art the Image gets base-aligned with the
(lowest) baseline of the label in it's neighboring VBox.

One might need the choice to align the Image with the VBox
directly, with the "Label", or with the lower "Other Content",
this indicates to me that we might end up with some declarative
API which might define anchor points with symbolic strings.

I hope these added thoughts are constructive for you, thanks
for getting the baselines ball rolling, too :)

Cheers,
          -Tristan




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]