Documentation: Layout Managers

| Documentation: Layout Managers [Remove Frame]

Placing Widgets Automatically

Making an attractive layout for a Dialog or Window is an important consideration in design of a user interface. Setting windows at specific x,y coordinates, and specifying explicit dimensions allow the GUI designer full control over the placement of each Control. However, this is very tedious and time-consuming. Also, what if the labels on buttons change, or if the user wants to use a bigger font?

For these reasons, the preferred method for placing GUI Controls on windows in FOX is through the use of so-called Layout Managers. A Layout manager is a widget whose primary purpose is to arrange GUI Controls contained inside of it in a certain way. This even includes other Layout Managers! In fact, Layout Managers may be nested arbitrarily!

Different layout managers arrange their children in different arrangements, for example, from left-to-right, top-to-bottom, in a grid, or even all on top of one another. Most layout managers also allow for explicit placement of their children, using hard-coded coordinates.

The benefits of this approach vis-a-vis a precise and explicit placement is that:

1. It takes the tedium out of placing GUI Controls; the application programmer does not concern him or herself with specific coordinates.
2. GUI Controls are automatically arranged correctly, even if button labels are changed, or users choose bigger fonts.
3. Layouts may be recalculated intelligently when a user resizes the window.
4. It makes it easy to accomodate and place Controls which are created automatically under program control, for example in GUI's created from database tables.

In FOX, you determine the arrangement of a GUI Control by selecting the appropriate Layout Managers, and a combination of Packing Styles passed to the Layout Manager, as well as a combination of Layout Hints passed to the GUI Control being arranged. Thus, virtually every conceivable arrangement can be achieved simply by nesting the appropriate layout managers in a certain way.

Basic Layout Patterns

FOX supports a number of general-purpose layout managers. The desired arrangement of GUI controls determines which layout manager is the most appropriate for the job; the following table lists the most commonly used layout managers and their layout arrangement:

FXPacker | The Packer layout widget places its GUI elements in its interior rectangle, placing each child against one of the four sides. As each child is placed, the size of the rectangle is reduced by the space taken up by the child. If a child is placed against the left or right, the interior rectangle's width is reduced; if the child is placed against the top or bottom, the height is reduced. Children may be of any type, including other layout managers.
FXTopWindow | The TopWindow operates like an FXPacker window. For simple dialogs and toplevel windows, no additional layout managers may be needed in many cases, as the TopWindow's layout characteristics may be sufficient.
FXHorizontalFrame | The HorizontalFrame layout manager packs its children horizontally from left to right (or right to left).
FXVerticalFrame | The VerticalFrame layout manager packs its children vertically, from top to bottom or vice versa. It behaves similar to the HorizontalFrame layout manager.
FXMatrix | The Matrix layout manager arranges its children in rows and columns. An FXMatrix widget can operate in both column-oriented as well as row-oriented mode. Normally, the Matrix layout manager operates row-wise. Based on the number of rows, the Matrix layout determines the width of each column and the height of each row, then arranges the children in the space allotted, observing the child's layout hints as much as possible.
FXSwitcher | The Switcher layout manager places its children exactly on top of each other; it ignores most of the layout hints provided by each child. You typically use a layout manager like the switcher to save screen real-estate, by placing for example several control panels on top of each other, and bringing the right one on top depending on the context.
FXGroupBox | The GroupBox is a layout manager that provides identical layout facilities as the Packer. In addition, the GroupBox draws an attractive border around its contents, and provides an optional caption.
FXSplitter | The Splitter layout manager divides some area of the screen horizontally or vertically. The divider bars can be repositioned by the user, so that depending on what the user is doing, he or she may give one or the other partition more screen space.
FX4Splitter | The Four-way splitter divides its contents into four subframes, like a four-paned window. The user can interactively adjust the dividers to change the division. Unlike the simple splitter, the subdivision of the four-way splitter is fractional, i.e. the subframes are resized proportionally if the entire four-way splitter is resized.
FXSpring | The spring is typically used inside a FXHorizontalFrame or FXVerticalFrame. As its name implies, it stretches and compresses like a spring of a certain length. Different springs of different lengths are typically placed side-by-side in a FXHorizontalFrame, allowing for a fixed-proportion arrangement, e.g. a 60:40 split.

Layout Padding

Apart from a certain arrangement of their children, Layout Managers also provide an amount of interior padding between the children, and between the children and the borders. The amount of padding may be zero, but it is typical to accept the default value, which is a few pixels; this usually looks better.

The padding parameters are defined as below:

Note that for most Layout Managers, the padding options are passed as the last 6 parameters of the constructor. Since default-values are given, there is often no need to supply explicit values for them.

How Layout Works

All widgets in FOX are organized into a widget hierarchy or widget-tree. Widgets are roughly classified as Composite widgets, and Simple widgets. Composite widgets can have child widgets, whereas Simple widgets are the most basic type of widget.

One widget is at the top of the widget hierarchy: the RootWindow widget. This special widget represents the background screen on your display. Widgets below the RootWindow widgets are called Shell widgets. Shell widgets are positioned and resized directly by the end-user, typically through resize handles and title-bars provided by a Window Manager.

As a user resizes a Shell widget, layout needs to be performed to reposition each widget in that Shell so as to maintain the proper arrangement. Hence, we refer to the layout process as going top-down, i.e. proceeding from widgets higher up in the widget tree downward toward the leaves of the tree.

Sometimes, however, widgets may want to change size of their own accord. For example, an application changes the text on a Label widget, making it larger. Changing a widget's size demands that its immediate parent be notified, as widgets are arranged by their parents. Depending on the arrangement, the widget's parent may decide that it, too, may need to change size, and in its turn notify its parent. Thus process goes on all the way till some widget is encountered whose size is not affected by the change. Thus, we refer to the recalculation process as going bottom-up.

In the course of recursing upwards, all widgets are marked for later layout. The upward traversal will typically be stopped because of one of the following reasons:

1. The widget is a Shell widget. Shell widgets are resized by the user only, so the size will not change, and layout will have to be performed as well as possible given the size of the Shell widget.
2. The widget is a ScrollWindow widget. A ScrollWindow does not have to grow or shrink when its child does:- it just adjusts the scrollbars to reflect the new child size, and layout the child properly.
3. The widget is able to accomodate the child's new size. For example, it may be that the child that changed was not the determining factor that caused the parent's size; in such a case, no further marking needs to take place.

During idle processing, all marked widgets will be laid out by the system, and their mark-flags will be reset. Thus, during processing of a user-event, any number of things may be changed in the User Interface; still, only one pass will be performed to rearrange all widgets again. This is one of the reasons for FOX's fast performance for large-scale graphical user interfaces.

About Default Sizes

All widgets may be requested to compute theirdefault size. Layout widgets will use this information to determine their own default size. Mostly, layout managers will try and ensure that a child-widget will not be made smaller than its reported default size. Note that the default size is normally the minimum size that is sensible for a widget, but that a widget may potentially become smaller than its default size!
For most types of Controls, the default size is computed based on the content, such as a text label and icon, plus borders and perhaps some interior padding; most Controls have a border, which may be 0, 1, or 2 pixels wide, and an interior padding around the top, bottom, left, and right side. The interior padding provides some spacing around the content for visual aesthetics. For Layout Managers, the default size is computed based on the arrangement of the children, their default sizes, its own border, interior padding, and inter-child spacing.

Layout Hints

With layout hints, widgets can inform their parent layout manager of certain desired positioning and sizing requirements. Since they are just hints, these may not always be observed. Generally, however, the layout widgets will try and do their best to observe the hints insofar as feasible.

The absence of a specific hint usually indicates that a default value is to be chosen. So in many cases, you do not need to fully specify any hints at all! In complicated situations, however, you may have to specify many of these hints. The FOX toolkit has defined the hints in such a way that the most common situations require the fewest hint-flags; for example, normally, Layout Managers are filled from top to bottom, and left to right. Thus, you wouldn't have to specify these hints if this is the case!

We will subsequently describe the layout hints and their effect on the typical layout process; we have indicated the default option between parentheses; it is usually the case that the default options do not have to be specified explicitly.

Layout Hint: | Where: | Effect:
--- | --- | ---
LAYOUT_SIDE_TOP (default) LAYOUT_SIDE_BOTTOM LAYOUT_SIDE_LEFT LAYOUT_SIDE_RIGHT | FXPacker, FXGroupBox, FXTopWindow, only | If you specify one of these four options, the child widget will be stuck to the top, bottom, left, or right, respectively in the layout manager cavity. The cavity's size will be reduced by the amount lopped off by the packed widget. LAYOUT_SIDE_TOP/LAYOUT_SIDE_BOTTOM will reduce the height of the cavity, and LAYOUT_SIDE_LEFT/LAYOUT_SIDE_RIGHT will reduce the width. For other composite widgets, these options may not have any effect
LAYOUT_FILL_ROW LAYOUT_FILL_COLUMN | FXMatrix only | If LAYOUT_FILL_COLUMN is specified for all child widgets in a certain column in of a Matrix layout manager, the whole column can stretch if the Matrix itself is stretched horizontally. Analoguously, if LAYOUT_FILL_ROW is specified for all child widgets in a certain row, the whole row is stretched if the Matrix layout manager is stretched vertically.
LAYOUT_LEFT (default) LAYOUT_RIGHT | all | The widget will be placed on the left-side, or right side of the space remaining in the container. When used for a child of FXPacker FXGroupBox, or FXTopWindow, the hint will be ignored unless either LAYOUT_SIDE_TOP or LAYOUT_SIDE_BOTTOM is specified.
LAYOUT_TOP (default) LAYOUT_BOTTOM | all | The widget will be placed on the top-side or bottom-side of the space remaining in the container. For a child of FXPacker etc., these options will only have effect if either LAYOUT_SIDE_RIGHT or LAYOUT_SIDE_LEFT is specified.
LAYOUT_FIX_X LAYOUT_FIX_Y | all | Either none, one, or both of these hints may be specified. The LAYOUT_FIX_X hint will cause the parent layout manager to place this widget at the indicated X position, as passed on the optional arguments in the widgets constructor argument list. Likewise, a LAYOUT_FIX_Y hint will cause placement at the indicated Y position. Note that the X and Y position is specified in the parent's coordinate system.
LAYOUT_FIX_WIDTH LAYOUT_FIX_HEIGHT | all | These options will fix the widgets width (resp. height) to the value specified on the constructor. You can change the widget's size using setWidth() and setHeight(), under program control; however, the layout manager will generally observe the specified dimensions of the widget without trying to modify it (unless other options override).
LAYOUT_MIN_WIDTH (default) LAYOUT_MIN_HEIGHT (default) | all | Either none, one, or both of these hints may be specified. You will almost never specify these options, except perhaps for code legibility. If LAYOUT_FIX_WIDTH or LAYOUT_FIX_HEIGHT are not specified, these options will cause the parent layout widget to use the default (or minimum) width and height, respectively.
LAYOUT_CENTER_X LAYOUT_CENTER_Y | all | The widget will be centered in the x-direction (y-direction) in the parent. Extra spacing will be added around the widget to place it at the center of the space available to it. The widget's size will be its default size unless LAYOUT_FIX_WIDTH or LAYOUT_FIX_HEIGHT have been specified.
LAYOUT_FILL_X LAYOUT_FILL_Y | all | Either none, one, or both of these hints may be specified. LAYOUT_FILL_X will cause the parent layout manager to stretch or shrink the widget to accomodate the available space. If more than one child with this option is placed side by side, the available space will be subdivided proportionally to their default size. LAYOUT_FILL_Y has the identical effect on the vertical direction.

General Purpose Layout Managers

All general-purpose layout widgets have interior padding, to offset their child-widgets by some amount from the left, right, top, and bottom edge of the layout manager's interior edges, as well as horizontal and vertical spacing, which is extra space placed between child-widgets to prevent them from being stuck too close together.
As usual, sensible default values are automatically supplied in the optional arguments on the constructor argument list so that in many cases, you will not need to specify specific values yourself.< All layout managers support the so-called packing styles:

Packing Styles

The Layout managers support a number of packing styles which can influence the way their children are arranged. These packing style flags override hints that child widgets may supply. The following packing styles are available:

Packing Style: | Effect:
--- | ---
PACK_UNIFORM_HEIGHT | The PACK_UNIFORM_HEIGHT packing style causes the Layout Manager to compute the height of the tallest of its children, and then subsequently use this as the height for the remaining layout computations. You can use this option to easily force different widgets to be the same height.
PACK_UNIFORM_WIDTH | Like PACK_UNIFORM_HEIGHT, PACK_UNIFORM_WIDTH forces each widget to be the same width. The widget's own preferences such as LAYOUT_FIX_WIDTH are overridden.

Matrix Layout Widget

The Matrix layout widget positions its children in nice rows and columns. Besides the common General Purpose Layout options, Matrix also supports the following layout styles:

Matrix Style: | Effect:
--- | ---
MATRIX_BY_ROWS (default) | The MATRIX_BY_ROWS is the default, and usually does not need to be specified.
MATRIX_BY_COLUMNS | When MATRIX_BY_COLUMNS is specified, the number of columns of the matrix is fixed, and equal to the number specified on the constructor argument list; the number of rows is determined by the total number of children. Conversely, if MATRIX_BY_COLUMNS is not specified, the number of rows is fixed and the number of columns is determined by the number of children.

In either case, Matrix places children top to bottom, left to right (left to right, top to bottom for MATRIX_BY_COLUMNS), and determines the height of each row and width of each column as follows:

1. If a column contains children which all have set the property LAYOUT_FILL_COLUMN, the whole column will be considered stretchable. The amount of stretch is proportionally disbursed between all stretchable columns.
2. If a row contains children which all have set the property LAYOUT_FILL_ROW, the whole row is stretchable.
3. If a column is not stretchable, the column's width will be determined by the widest child in the column (or the widest child of all of them if the Matrix has PACK_UNIFORM_WIDTH has been specified).
4. If a row is not stretchable, the row's height is determined by its tallest child in the row (or tallest child of all children if PACK_UNIFORM_HEIGHT).

The Matrix widget then arranges each child in its appropriate row/column. Within the column, a child that is not stretched may be centered in the column, or placed against the left or right edge of the column; likewise, within the row, a child that is not stretched may be centered in the row, or placed against the top or bottom. Between each pair of columns, the Matrix layout manager adds a small amount of horizontal spacing. Between each pair of rows, it applies some vertical spacing.
Around the top, bottom, left, and right, the Matrix provides the usual interior padding for visual aesthetics.

The Matrix layout manager ignores hints for a specific X or Y position of each child.

Packer Layout Widget

The Packer layout widget places its children inside a cavity, packing them against one of the four sides, until the entire space is filled up. Which side of the Packer a child is placed against is determined specifying one of the side hints:

- LAYOUT_SIDE_TOP. Forces placement of the child against the top side of the Packer. This is the default, and may not have to be specified explicitly.
- LAYOUT_SIDE_BOTTOM. Forces placement of the child against the bottom.
- LAYOUT_SIDE_LEFT. Places the child against the left side of the Packer.
- LAYOUT_SIDE_RIGHT. Places the child against the right side of the Packer.

Each time a child is placed against one of the four sides on the inside of the packer, it decreases the packer's interior by some amount. If the child is placed at the top or bottom, the height is decreased by the child's height; if placed against the left or right side, the width is reduced by the child's width.

Even so, the Packer tries to observe as many layout hints as feasible. Thus, for a child being packed on the left or right, the hints LAYOUT_TOP and LAYOUT_BOTTOM are still observed and cause the widget to be placed against the top or bottom of the packer. Likewise, the LAYOUT_FILL_Y and LAYOUT_CENTER_Y are also observed, and cause the child to stretch or center in the packer's cavity.

Analaguous to the above, when a child is packed against the top or bottom, the Packer properly observes the LAYOUT_LEFT, LAYOUT_RIGHT, LAYOUT_FILL_X, and LAYOUT_CENTER_X hints.

There is one special case: When packing the last child, the Packer observes both LAYOUT_CENTER_X, LAYOUT_CENTER_Y, LAYOUT_FILL_X, and LAYOUT_FILL_Y. This will cause the Packer to completely fill the remaining space of the cavity with the last child widget.

You can make use of this feature in a typical application by defining your Main Viewing area as the last widget to be placed.

Layout Suggestions

FOX allows you either perform automatic layout or explicitly control over widget placement by specifying positions and sizes. The automatic layout has the advantage of accomodating changes of fonts, or changes in text labels or icons without any effort. This is especially interesting because it makes it quite easy to build GUI's under program control (e.g. by reading a configuration file or database schema).

However, it takes a bit more forethought to figure out how various widget- layouts are structured, i.e. how various layout managers and controls are to be arranged to achieve the desired effect. Some hints:

1. Work areas like multi-line text or OpenGL windows should be flexible both horizontally and vertically. This allows the user to grow the work area if desired.
2. Simple text fields, sliders, dials, and so on should be stretchable horizontally if possible:- sliders and dials are more accurate if bigger, and you can see more of the text. Of course for vertical sliders and dials, you would want to allow vertical stretching.
3. Buttons need not be stretchable, there is no point. However, it is pleasing to keep proper proportions in the dialog. This means keeping certain buttons the same size using PACK_UNIFORM_WIDTH, and centering them under the dialog with LAYOUT_CENTER_X under the content, for example.
4. Nice tabular layouts can be achieved quickly using the Matrix layout manager. Selected rows and columns may be allowed to stretch vertically and horizontally.
5. Because sizes are figured from the inside-out, it is better to force a few selected widgets like scroll areas to be a certain size and let the system figure the size of the dialog, rather than try and set the size of the dialog and hope that the scroll areas wind up big enough.
6. You can use a borderless Frame widget as a "spacer" if some extra space is needed.

Try to design dialogs to be resizable, because it is almost always the case that text fields, sliders, and list widgets would be able to take advantage of a larger screen area.

Copyright © 1997-2005 Jeroen van der Zijp