JustUI/doc/layout.md

88 lines
4.2 KiB
Markdown
Raw Normal View History

2021-03-12 16:19:43 +01:00
# JustUI: Space distribution and layout
Layout is the process through which space is allocated to widgets, which are
then sized and positioned.
## Content widgets and containers
Widgets in a scene usually fulfill one of two roles:
* **Content widgets** such as labels, input fields, or menu browsers, display
the interface and receive events. They often have no children, and do their
own rendering.
* **Containers** such as rows and columns, stacks, or grids, organize other
widgets so they align nicely and don't overlap. The widgets they organize are
their children; they themselves often perform no rendering.
JustUI does not enforce this separation, and a single widget can both handle
contents and organize children. But the features are usually designed with one
of the two roles in mind.
## Layouts
Layouts are parameters that can be attached to widgets to automatically size
and position their widgets in a useful fashion. This is mostly designed for
containers. There are currently 4 types of layouts:
* **Horizontal boxes** and **vertical boxes** arrange children in a row or a
column, respectively. Each widget gets its desired size; if there is space
left, widgets can expand according to stretch parameters (more on that
later).
* **Stacks** arrange all the widgets on top of each other. Only one widget is
visible at a time. This is useful for tabbed interfaces.
* **Grids** arrange all widgets in a grid. (TODO: WIP)
A widget that does not have a layout needs to manually determine its own
desired size as well as the position its children.
## The layout process
The layout process has two phases.
1. **Size estimation.** In the first phase, each widget declares a desired
size. This size often depends on the size of the children, so this phase
proceeds bottom-up: first the children declare their desired sizes, then the
parents deduce their own desired sizes, and so on.
2. **Space distribution.** In the second phase, space is distributed by the
parents to the children. If the parents have more available space than the
children request, extra space is distributed as well. This phase is
top-down: first the root distributes the available space to its children,
then these children split their shares between their own children, etc.
All of this proceeds automatically unless some widgets cannot provide a natural
size (or only a useless one), in which case the user should give a hint.
## Internals
During the first phase, the content size of each widget is evaluated by either
the layout's `csize()` function, or the polymorphic widget's `csize()`
override. Then `jwidget_msize()` adds in the geometry and stores the margin-box
size in the `w` and `h` attributes.
During the second phase, `jwidget_layout_apply()` distributes the space by
dispatching to the layout's `apply()` function, or the polymorphic widget's
`apply()` override. It proceeds recursively in a depth-first, prefix order.
## Layout control
The widget type provides a natural content size, but the user has the final say
in the size of any widget. Any widget can have a minimum and maximum size
specified, and every layout guarantees that the allocated size falls in this
range (note that limits are examined during the second phase, and the natural
content size does not need to be in the acceptable range).
Additionally, the user can provide stretch rates indicating whether the widget
can use more space horizontally and vertically. When there is space to
distribute and several widgets are competing to use it, space is allocated in
proportion to stretch rates. For instance, a widget with double the stretch
rate of its competitors will get twice as much space as them.
In certain occasions, one might want to disregard the natural content size
entirely and distribute space based only on stretch rates, for instance to
split a screen in evenly-sized columns even when the contents of the columns
have different natural sizes. In this case, one can set the columns to a fixed
width of 0 while enabling stretching-beyond-limits. Stretching beyond limits
will allow the widgets to grow despite being of fixed size, and because they
all start with the same width of 0, they will all end up with the same size.