The purpose of the automatic layout system is to relieve the application programmer of the task to specify the exact position and size of each GUI fudget. This task has several dynamic aspects. To start with, it depends on factors that are not known until the program has started. For example, the size of a text drawn in a label fudget depends on the the font and size chosen, and can only be determined after the label fudget has communicated with the X server. Individual GUI fudgets can also change their size at any time, and the user might resize a shell window. Both these activities may imply that a number of GUI fudgets must change their position and size.
The layout system also simplifies the implementation of the individual GUI fudget, in that it does not have to worry about the place and position of other GUI fudgets. It must only specify an initial request for a size, and the layout system will allocate a place and actual size.
The implementation of the layout system operates by moving and resizing rectangular units corresponding to the group fudgets (Section 22.1). Remember that a group fudget basically consists of a stream processor controlling an X window, possibly with a number of group fudgets inside it. Each group fudget also contains a piece of the layout system, which is responsible for the placement and sizing of each immediate subgroup fudget. The responsibility is only one level deep: a group fudget does not control any group contained in any of its groups. As an example, the group corresponding to k1 in Figure 51 is responsible for the layout of k2 and k4 (but not k 3).
This division of responsibility is natural, since a group is easily
placed and resized by the single Xlib command ( ConfigureWindow
). All subwindows inside the group will follow and
keep their relative positions.
The mechanism of the layout system can be studied by looking at the message traffic between a group fudget and its immediate subgroups.
The group fudget has a filter (other filters are described in
Chapter 24), called autoLayoutF
, which listens for layout
messages that are output on the low-level streams from the subgroups.
The subgroups decide what sizes they need, and output a layout request.data LayoutMessage = LayoutRequest LayoutRequest | LayoutName String | LayoutPlacer Placer | LayoutSpacer Spacer ...
The fielddata LayoutRequest = Layout { minsize :: Size, fixedh, fixedv :: Bool }
minsize
is the requested size, and fixedh
(fixedv
) being true specifies that the size is not
stretchable in the horizontal (vertical) direction. (Some placers
use this information to allocate extra space to stretchable boxes
only.)
The layout filter also receives placers and spacers that the
programmer has wrapped around subgroups. Since all layout messages
are tagged with a path, the layout filter can associate the placers
and spacers with the wrapped subgroups, by analysing the paths. The
constructor LayoutName
is used in a similar way to associate
a subgroup with a name.
The placers and spacers are functions that decide the actual layout. A placer operates on a list of layout requests, yielding one single request for space needed for the placement of all the associated subgroups. Looking back at the discussion of boxes in Section 11.1, we will recognise that there will be one layout request corresponding to each box.
In contrast to the placers, a spacer takes a single request as an argument, and the layout filter maps it on all requests corresponding to the enclosed boxes associated with the spacer, yielding the same number of requests.
As can be seen from these types, placers and spacers also return a residual function, which we will describe below.type Placer = [LayoutRequest] -> (LayoutRequest, Rect -> [Rect]) type Spacer = LayoutRequest -> (LayoutRequest, Rect -> Rect)
Having collected layout requests and having applied the layout functions to them, the group fudget must decide on one single layout request to output. Since programs should work even if no layout is specified by the programmer, a default placer is wrapped around the subgroups.
The default placer used is called autoP
, which picks a suitable
layout based on the layout requests at hand. In the current
implementation, it simply chooses between verticalP
and
horizontalP
, based on two preferences:
Having produced a single layout request, the group fudget outputs
it, and it will be handled by the enclosing group, unless the group
is a shell group. In this case, the minsize
field in the
request is used to set the size of the shell window.
The XEvent
type includes constructors that are used to
report changes in layout to the GUI fudgets:
The propagation of these layout events start in the shell group fudget. When the shell window has been resized, the X server sends adata XEvent = ... | LayoutPlace Rect | LayoutSize Size
ConfigureNotify
event containing the new size to the shell
group fudget. Note that this event is generated regardless of
whether the resize operation was done by the program (as a result
of a layout request) or the user (via the window manager). Anyhow,
the shell group fudget generates an event of the form LayoutPlace (Rect 0 s)
, to the layout filter. This informs
the layout filter that the rectangle from the origin to s is
available for the subgroups. Now, the layout filter applies the
residual placers and spacers to this rectangle in a reversed
pattern. Each residual placer will yield a list of rectangles,
where the elements correspond to the list of requests (and thus to
the boxes) that was fed to the original placer. Similarly, residual
spacers are mapped over rectangles to adjust positions of the
associated subgroups.
When this reversed process is finished, the layout filter outputs
one LayoutPlace
message to each subgroup, which will move
itself accordingly, pass the message to its layout filter, and so
the process goes on recursively.
When a group receives a LayoutPlace
message, it also sends a
LayoutSize
message to the kernel stream processor, so that
it can adjust the graphical content of its window to the new
geometry. Note that the kernel only needs to know the size of its
window, and not its place. This is due to the fact that all window
operations use local coordinates.