The CAPI provides various layout classes which allow you to combine multiple window elements in a single window. This chapter provides an introduction to the different classes of layout available and the ways in which each can be used.
Layouts are created just like any other CAPI element, by calling make-instance. Each layout needs to have a description which is a list of the CAPI elements it contains. The description can be supplied via the :description
initarg. It can also be supplied or modified later by calling (setf layout-description)
in the layout's process. The description is interpreted by interpret-description as specifying a list of elements which are the "children" of the layout. The layout groups its children on the screen and specifies their geometry (x and y coordinates of top-left corner, width and height).
Only CAPI elements can be layout children. In this chapter "children" or "child" refers only to elements of these types:
For example, to put elements one above the other you make an instance of class column-layout with the elements as its description:
(defun put-in-a-column (list-of-elements) (make-instance 'column-layout :description list-of-elements))
Since the result is a layout, you can put it in an interface and display it:
(defun display-in-a-column (list-of-elements) (display (make-instance 'interface :layout (put-in-a-column list-of-elements)))) (display-in-a-column (list (make-instance 'text-input-pane :text "Text input pane") (make-instance 'push-button :data "Button"))) (display-in-a-column (loop for x below 10 collect (make-instance 'push-button :data (format nil "Button No. ~d" x))))
Layout themselves are subclasses of simple-pane, and hence can be children of other layouts, creating a hierarchical "tree" of layouts with other types of children as the "leaves". This is the normal way of laying out all the elements inside an interface. interface is also a subclass of simple-pane and can appear in the hierarchy, though usually interface is used only for the top-level window.
In general, the layouts need to know their childrens' geometrical requirements. These requirements are referred to as "constraints" and include the minimum and maximum width and height. Some of the child classes have default constraints, for example text-input-pane by default has both minimum and maximum height which allows showing one line, taking into account the height of the font. Most child classes do not have default constraints, and in effect have a minimum dimension of 0 and no maximum. Quite often that is good enough, but not always.
You can override the default constraints of an element by specifying geometrical "hints" (the word "constraint" is sometimes used to refer to the hint). Hints can be specified in many ways, for example the minimum width can be specified as enough to display 30 characters. Geometrical hints are typically specified by initargs when making a pane, but you can also set them dynamically. See 6.4 Specifying geometry hints for details. In most cases, specifying the hints is sufficient (once you specify the hierarchy of layouts).
The function get-constraints computes the constraints in pixels based on the hints or the defaults, and returns the min/max of the width and height. Note that the result of get-constraints is dependent both on the hints themselves and other factors. For example, if the minimum width of an element is specified as "30 characters", changing the font of the element will cause get-constraints to return a different value. For more complex computations, it is also possible to define a calculate-constraints method, but in most cases the geometry hints are enough.
The layouts in general use get-constraints to get the constraints of their children, and take them into account when calculating the geometry of the elements and its own implicit constraints. For example, a row-layout puts elements side-by-side, and if it has two children with minimum width and height of 100, it will have an implicit minimum width of 200 and implicit minimum height of 100. The implicit constraints are used by get-constraints on the layout itself (by its parent), unless they are overridden by geometry hints or calculate-constraints on the layout.
The process of laying out starts at the top of the hierarchy, with the outer layout calling get-constraints on its children. If any of the children is a layout itself, it calls get-constraints of its children. Thus the get-constraints call is propagated down the hierarchy to all the tree, and the results are propagated back. Then the top layout lays out its children, that is it tells them their geometry, and again this is propagated down by each child which is a layout itself.
When a layout lays out its children, its uses its own geometry, the children's constraints and a layout-specific algorithm, which is implemented by calculate-layout. Thus when the documentation describes a layout of some class as "laying out its children in some way" it really means that this is what the applicable method of calculate-layout tries to achieve. Note that calculate-layout does not necessarily obey the constraints, and even the methods that intend to obey the constraints may fail to do so. For example, a row-layout with two children each of minimum width 100 which is given a width of 150 pixels will give only 50 to the second child. Conversely, when the layout has more space that the minimum required it usually distributes space between the elements that are not constrained by a maximum.
calculate-layout records the layout that it computed by setting the x y width and height in the geometries of the children (using with-geometry). The system then displays the children with the new geometry.
The hierarchy of layouts is laid out from the top layout of the top level interface when the interface is being displayed. After that, whenever the program makes a change to any element which may change its constraints, the system goes up the hierarchy until it finds a layout that it can tell is not going to need to change its constraints, and then lays out the children of that layout, as described above.
You can tell CAPI that the constraints of a pane may have changed and need to be recomputed (and hence maybe part of the hierarchy needs re-layout) by calling invalidate-pane-constraints.
Once again, you should make sure you have defined the test-callback
function before attempting any of the examples in this chapter. Its definition is repeated here for convenience.
(defun test-callback (data interface) (display-message "Data ~S in interface ~S" data interface))
CAPI User Guide and Reference Manual (Windows version) - 01 Dec 2021 19:33:49