Widget Layout |
Placing Widgets.
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 in it in a certain way in its interior; this even includes other Layout Managers! The benefits of this approach vis-a-vis a precise and explicit placement is that:
Note that FOX allows nesting of Layout Managers; a nested Layout Manager can thus have both a Packing Style as well as Layout Hints!
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. Finally, if its children are RadioButtons, it forces at most one of these to be checked. |
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. |
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:
About Default Sizes.
All widgets may be requested to compute their
default
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, FXTopLevel, 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 FXTopLevel, 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:
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:
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.