FOX Community

Recent site activity

Tutorials‎ > ‎

Basic Layout

Introduction

In 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 Construction

Most 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 Flags

Even 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 widgets

In 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.