IntroductionIn FOX placement of widgets is mostly done using layout managers.
Layout managers simplify the task of placing and maintaining the
position of widgets on your window. You don't have to keep track of
window resizes and with the right layout flags widgets will
automatically resize too. Note that FOX still allows programmers to
place widgets in a fixed position, although this method is discouraged.
Compared to some other toolkits like, QT or GTK, layout managers
have been tightly integrated into FOX from day one. You create layout
managers the same way you create ordinary widgets like Buttons or
TextFields. In essence, layout managers are just widgets themselves so.
In FOX there are two types of widgets. Composite widgets and normal
widgets. Composite widgets may contain child widgets, while normal
widgets don't have any child widgets. Layout managers are examples of
composite widgets. Except for the toplevel windows (which are also some
sort of composite), composite and normal widgets all share the same
basic setup; They need to be placed inside some other composite widget
(its parent). And as you might have noticed, this allows for nesting
layout managers.
Lets look at some example code on how we create layout managers:
FXHorizontalFrame * frame = new FXHorizontalFrame(dialog); new FXButton(frame,"Push Me");
Now if we create another button:
new FXButton(frame,"Push Me Too: I'm placed next to Push Me"); The layout manager puts the second button next to the first
button. Ofcourse instead of a buttons we could also have placed a
different layout manager inside of the horizontal frame: FXHorizontalFrame *frame = new FXHorizontalFrame(dialog) new FXVerticalFrame(frame); new FXVerticalFrame(frame);
Now we have two columns, one can have buttons while the other may contain an opengl widget. Widget ConstructionMost widgets and layout managers have a similar constructors:
widget(parentwindow,.....,target,messageid,options,x,y,w,h,pl,pr,pt,pb);
layoutmanager(parentwindow,.....,target,messageid,options,x,y,w,h,pl,pr,pt,pb,hs,vs);
Where:
x - x position of widget y - y position of widget w - width of widget h - height of widget pl - padding left pr - padding right pt - padding top pb - padding bottom hs - horizontal spacing vs - vertical spacing
The x,y,w and h parameters are ignored unless the explicit layout flags are passed (see "Explicit Placement" down below). The hs and vs are only available if the widget is a layout manager.
Layout Flags
In FOX, layout flags are used to control the position of widgets inside
layout managers. It is important to remember that layout managers
decide how widgets are going to be placed on screen. Layout flags
placed on the child widgets of a layout manager are merely hints for
the layout manager whenever it is doing its layout. In the end, a
widgets placement is determined by the parent of that widget. In other
words if you specify that a widget should stretch itself in the X
direction (LAYOUT_FILL_X), it is the parent widget that actually will
do the stretching if possible.
Some of the layout managers also have their own flags that
influence their placement policy for child widgets. You can recognize
these hints easily since their names don't start with "LAYOUT_".
Now, lets have a look at what type of layout managers FOX provides:
General purpose layout managers:
- FXHorizontalFrame
- FXVerticalFrame
- FXPacker
- FXMatrix
- FXGroupBox
- FX4Splitter
- FXSplitter
- FXSpring
In addition the following widgets also control the layout of their child widgets:
- FXMainWindow - behaves like a FXPacker
- FXDialogBox - behaves like a FXPacker
- FXDockBar
- FXDockSite
- FXStatusBar
- FXToolBar - behaves like a FXHorizontalFrame or FXVerticalFrame depending on how the toolbar is docked.
- FXMenuBar - behaves like a FXHorizontalFrame or FXVerticalFrame depending on how the toolbar is docked.
Horizontal-Vertical Layout
FXHorizontalFrame and FXVerticalFrame provide the basic layout needs
for most FOX programs. They place widgets either horizontally or
vertically. There are special layout flags that indicate to a layout
manager where a widget is to be positioned. The default for all FOX
widgets is LAYOUT_TOP and LAYOUT_LEFT. Therefore by default widgets in
FXHorizontalFrame and FXVerticalFrame are positioned from left to right
and top to bottom. You can change the positioning by using the
following flags
- LAYOUT_LEFT - place widget on the left
- LAYOUT_RIGHT - place widget on the right
- LAYOUT_TOP - place widget on the top
- LAYOUT_BOTTOM - place widget on the bottom
- LAYOUT_CENTER_X - place widget in the center of the available space
- LAYOUT_CENTER_Y - place widget in the center of the available space
As stated before LAYOUT_LEFT and LAYOUT_TOP are the default flags used for all widgets.
Horizontal Layout Flags
FXHorizontalFrame * frame = new FXHorizontalFrame(mainwindow,LAYOUT_FILL_X); new FXButton(frame,"Button 1"); new FXButton(frame,"Button 2"); new FXButton(frame,"Button 3"); new FXButton(frame,"Button 4");
Since we didn't specify any layout flags for the buttons, they're
placed from left to right in the order they were created. To change the
order of the buttons we could change the order in which we create the
buttons, but we can also change some of the layout flags. Watch what
happens when add a LAYOUT_RIGHT to button no. 2: FXHorizontalFrame * frame = new FXHorizontalFrame(mainwindow,LAYOUT_FILL_X); new FXButton(frame,"Button 1"); new FXButton(frame,"Button 2",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_RIGHT); new FXButton(frame,"Button 3"); new FXButton(frame,"Button 4");
As you can see button 2 has moved all the way to the right. If we would
specify LAYOUT_RIGHT to button 3 as well the following will happen: FXHorizontalFrame * frame = new FXHorizontalFrame(mainwindow,LAYOUT_FILL_X); new FXButton(frame,"Button 1"); new FXButton(frame,"Button 2",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_RIGHT); new FXButton(frame,"Button 3",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_RIGHT); new FXButton(frame,"Button 4"); Button 3 also moved to the right and placed before button 2. Remember first come, first serve. To illustrate, this would happen if you specify LAYOUT_RIGHT for all the buttons:
FXHorizontalFrame * frame = new FXHorizontalFrame(mainwindow,LAYOUT_FILL_X); new FXButton(frame,"Button 1",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_RIGHT); new FXButton(frame,"Button 2",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_RIGHT); new FXButton(frame,"Button 3",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_RIGHT); new FXButton(frame,"Button 4",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_RIGHT);
Vertical Layout FlagsEven though we're using a horizontal based layout manager, the vertical layout flags still can influence the position of widgets. This happens in cases when the layout manager has more vertical space than the widgets need. Let's go back to our four buttons and lets stretch the window and layout manager in the y direction: FXHorizontalFrame * frame = new FXHorizontalFrame(mainwindow,LAYOUT_FILL_X|LAYOUT_FILL_Y); new FXButton(frame,"Button 1"); new FXButton(frame,"Button 2"); new FXButton(frame,"Button 3"); new FXButton(frame,"Button 4");
As expected, using the default flags all the widgets are on the top. So let's see what happens when we add a LAYOUT_BOTTOM to two of our buttons: FXHorizontalFrame * frame = new FXHorizontalFrame(mainwindow,LAYOUT_FILL_X|LAYOUT_FILL_Y); new FXButton(frame,"Button 1"); new FXButton(frame,"Button 2",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_BOTTOM); new FXButton(frame,"Button 3"); new FXButton(frame,"Button 4",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_BOTTOM);
And adding the LAYOUT_CENTER_Y flag: FXHorizontalFrame * frame = new FXHorizontalFrame(mainwindow,LAYOUT_FILL_X|LAYOUT_FILL_Y); new FXButton(frame,"Button 1"); new FXButton(frame,"Button 2",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_BOTTOM); new FXButton(frame,"Button 3",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_CENTER_Y); new FXButton(frame,"Button 4",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_BOTTOM);
Packer Layout
FXPacker places its children to either the top, bottom, left or right
side of its available interior space. There are 4 layout flags
available to specify to which side of the packer a widget is placed.
- LAYOUT_SIDE_TOP - Place widget to the top side in the packer.
- LAYOUT_SIDE_BOTTOM - Place widget to the bottom side in the packer.
- LAYOUT_SIDE_LEFT - Place widget to the left side in the packer.
- LAYOUT_SIDE_RIGHT - Place widget to the right side in the packer.
The packer will go over each child widget and places it to one of
its sides. Then uses the space left over to determine where the next
one will be placed (and so on).
The next example shows you the packer at work. If you really want to
see how the packer works, add one button at a time and see what
happens:
FXPacker * packer = new FXPacker(main,LAYOUT_FILL_X|LAYOUT_FILL_Y); new FXButton(packer,"Button 1",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_X); new FXButton(packer,"Button\n2",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_Y|LAYOUT_SIDE_LEFT); new FXButton(packer,"Button 3",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_X|LAYOUT_SIDE_BOTTOM); new FXButton(packer,"Button\n4",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_Y|LAYOUT_SIDE_RIGHT); new FXButton(packer,"Button 5",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_X|LAYOUT_SIDE_TOP); new FXButton(packer,"Button 6",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_X|LAYOUT_SIDE_BOTTOM); new FXButton(packer,"Button\n7",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_Y|LAYOUT_SIDE_RIGHT); new FXButton(packer,"Button\n8",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_Y|LAYOUT_SIDE_LEFT); new FXButton(packer,"Button 9",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_X|LAYOUT_FILL_Y);

Once a widget is placed to one of the sides of the packe, there still
maybe space to move the widget around. To illustrate this, let's
replace the LAYOUT_FILL_Y for button 2 with LAYOUT_TOP, LAYOUT_BOTTOM
and LAYOUT_CENTER_Y:
LAYOUT _TOP
| LAYOUT_BOTTOM | LAYOUT_CENTER_Y | | | |
In addition to FXPacker, the following widgets also use the packing algorithm:
- FXGroupBox - Includes a additional title and border.
- FXMainWindow
- FXDialogBox
Matrix Layout
The matrix has two modes of operation: column mode and row mode. To specify which mode you want, pass the either of the following hints to FXMatrix: - MATRIX_BY_COLUMNS - column mode
- MATRIX_BY_ROWS - row mode (default)
In column mode, the number of columns is fixed and rows are automatically added if needed. In row mode the number of rows is fixed and columns are automatically added if needed. The second parameter of FXMatrix's constructor will specify how many rows or columns we want. To illustrate the difference, here is a example in which we place sixteen widgets in the matrix and specify 4 for number of columns/rows: MATRIX_BY_COLUMNS | MATRIX_BY_ROWS
| | |
Filling Rows and Columns
When you add a LAYOUT_FILL_X or LAYOUT_FILL_Y to a FXMatrix, you will
notice that the contents of the matrix is not filled in the specified
direction. Only the matrix itself is stretched.
It's easier to see when you add a border to your FXMatrix like for example FRAME_LINE:

The reason the content doesn't stretch is because we haven't specified
which columns or rows should stretch. By default if not specified, none
of the columns or rows will stretch. FXMatrix interprets two special
layout flags for this purpose:
- LAYOUT_FILL_COLUMN - Tells FXMatrix that this column is stretchable.
- LAYOUT_FILL_ROW - Tells FXMatrix that this row is stretchable.
In order to have a column or row stretch, all the (direct) child
widgets of FXMatrix in the same [row,column] would need to have the
LAYOUT_FILL_[ROW,COLUMN]. If one is missing the row or column won't
stretch.
Stretching a Column
For example, suppose we have the following code: FXMatrix* matrix = new FXMatrix(main,3,MATRIX_BY_COLUMNS|LAYOUT_FILL_X); new FXButton(matrix," 1 "); new FXButton(matrix," 2 "); new FXButton(matrix," 3 "); new FXButton(matrix," 4 "); new FXButton(matrix," 5 "); new FXButton(matrix," 6 "); new FXButton(matrix," 7 "); new FXButton(matrix," 8 "); new FXButton(matrix," 9 ");
To stretch the middle column, buttons 2,5 and 8 will need to have LAYOUT_FILL_COLUMN hint: ... new FXButton(matrix," 2 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_COLUMN); ... new FXButton(matrix," 5 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_COLUMN); ... new FXButton(matrix," 8 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_COLUMN);
Ofcourse the usual positioning/sizing layout flags will do their job as well:
... new FXButton(matrix," 2 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_COLUMN|LAYOUT_CENTER_X); ... new FXButton(matrix," 5 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_COLUMN|LAYOUT_FILL_X); ... new FXButton(matrix," 8 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_COLUMN|LAYOUT_RIGHT);

Stretching Multiple Columns
Multiple columns may be stretched as well. The available space is divided between the stretching columns:
... new FXButton(matrix," 2 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_COLUMN|LAYOUT_CENTER_X); new FXButton(matrix," 3 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_COLUMN); ... new FXButton(matrix," 5 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_COLUMN|LAYOUT_FILL_X); new FXButton(matrix," 6 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_COLUMN|LAYOUT_RIGHT); ... new FXButton(matrix," 8 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_COLUMN|LAYOUT_RIGHT); new FXButton(matrix," 9 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_COLUMN|LAYOUT_FILL_X);
Stretching a Row
Stretching rows works the same way. The direct children of FXMatrix in
the row that stretches should carry the LAYOUT_FILL_ROW hint (button
4,5 and 6):
FXMatrix* matrix = new FXMatrix(main,3,MATRIX_BY_COLUMNS|LAYOUT_FILL_Y); new FXButton(matrix," 1 "); new FXButton(matrix," 2 "); new FXButton(matrix," 3 "); new FXButton(matrix," 4 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_ROW|LAYOUT_BOTTOM); new FXButton(matrix," 5 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_ROW); new FXButton(matrix," 6 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_ROW|LAYOUT_FILL_Y); new FXButton(matrix," 7 "); new FXButton(matrix," 8 "); new FXButton(matrix," 9 "); Stretching Multiple Rows
Multiple rows may be stretched as well:
... new FXButton(matrix," 4 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_ROW|LAYOUT_BOTTOM); new FXButton(matrix," 5 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_ROW); new FXButton(matrix," 6 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_ROW|LAYOUT_FILL_Y); new FXButton(matrix," 7 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_ROW|LAYOUT_FILL_Y); new FXButton(matrix," 8 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_ROW|LAYOUT_BOTTOM); new FXButton(matrix," 9 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_ROW); Stretching Rows and Columns
Ofcourse, FOX allows rows and columns to be stretched at the same time:
FXMatrix* matrix = new FXMatrix(main,3,MATRIX_BY_COLUMNS|LAYOUT_FILL_Y|LAYOUT_FILL_X); new FXButton(matrix," 1 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_COLUMN|LAYOUT_FILL_X); new FXButton(matrix," 2 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_COLUMN|LAYOUT_CENTER_X); new FXButton(matrix," 3 "); new FXButton(matrix," 4 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_ROW|LAYOUT_FILL_COLUMN|LAYOUT_CENTER_X|LAYOUT_CENTER_Y); new FXButton(matrix," 5 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_ROW|LAYOUT_FILL_COLUMN|LAYOUT_FILL_Y|LAYOUT_FILL_X); new FXButton(matrix," 6 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_ROW|LAYOUT_BOTTOM); new FXButton(matrix," 7 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_ROW|LAYOUT_FILL_COLUMN|LAYOUT_FILL_Y); new FXButton(matrix," 8 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_ROW|LAYOUT_FILL_COLUMN|LAYOUT_RIGHT); new FXButton(matrix," 9 ",NULL,NULL,0,BUTTON_NORMAL|LAYOUT_FILL_ROW|LAYOUT_CENTER_Y);
Explicit Placement
The x, y, w and h parameters of widgets constructors are always ignored unless the proper layout flags are passed that enable these. The following flags can be used to enable explicit position and/or size:
- LAYOUT_FIX_X - the x parameter is used to position the widget in the x direction.
- LAYOUT_FIX_Y - the y parameter is used to position the widget in the y direction.
- LAYOUT_FIX_WIDTH - the w parameter determines the width of the widget.
- LAYOUT_FIX_HEIGHT - the h parameter determines the height of the widget.
- LAYOUT_EXPLICIT - A combination of LAYOUT_FIX_X|LAYOUT_FIX_Y|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT.
Note that the x and y are in the parents widget coordinate system.
Setting the minimal size of widgetsIn FOX you don't have to set the minimal size of widgets. If you don't specify any special layout flags like LAYOUT_FILL_X, most layout manager will use the default size of each widget. The default size can be queried using the getDefaultWidth() and getDefaultHeight() functions of each widget. For most widgets the default size is calculated based on its contents. For example, buttons and labels will measure the size of the specified text and icon.
The only widgets that don't provide a reasonable default size are widgets that are scrollable like FXText, FXList, FXTreeList etc. Basically anything derived from FXScrollArea. Now most of the time you would specify a LAYOUT_FILL anyway for these type of widgets (rational: if a dialog gets bigger, you want them to grow bigger as well). A lot of times surrouding widgets will give these a reasonable default size. Note that the best way to fix scrollable widgets that are too small, is to add a dummy frame with a fixed width or height.
|