VerticalLayout
and
HorizontalLayout
components are containers for
laying out components either vertically or horizontally,
respectively. Some components, such as Window
,
have a VerticalLayout
as the root layout, which
you can set with setLayout()
.
Typical use of the layouts goes as follows:
VerticalLayout vertical = new VerticalLayout (); vertical.addComponent(new TextField("Name")); vertical.addComponent(new TextField("Street address")); vertical.addComponent(new TextField("Postal code")); main.addComponent(vertical);
The text fields have a label attached, which will by default be placed above the field. The layout will look on screen as follows:
Using HorizontalLayout
gives the following layout:
The layouts can have spacing between the horizontal or vertical cells,
defined with setSpacing()
, as described in Section 5.3.3, “Layout Cell Spacing”. The contained components can be
aligned within their cells with
setComponentAlignment()
, as described in Section 5.3.2, “Layout Cell Alignment”.
You can use setWidth()
and
setHeight()
to specify width and height of a
component in either fixed units or relatively with a percentage.
The components contained within an ordered layout can be laid out in a number of different ways depending on how you specify their height or width in the primary direction of the layout component.
Figure 5.1, “Component Widths in HorizontalLayout
” above
gives a summary of the sizing options for a
HorizontalLayout
. Let us break down the figure
as follows.
If a VerticalLayout
has undefined height or
HorizontalLayout
undefined width, the layout
will shrink to fit the contained components so that there is no extra
space between them.
HorizontalLayout fittingLayout = new HorizontalLayout(); fittingLayout.setWidth(Sizeable.SIZE_UNDEFINED, 0); fittingLayout.addComponent(new Button("Small")); fittingLayout.addComponent(new Button("Medium-sized")); fittingLayout.addComponent(new Button("Quite a big component")); parentLayout.addComponent(fittingLayout);
If such a vertical layout continues below the bottom of a window (a
Window
object), the window will pop up a
vertical scroll bar on the right side of the window area. This way,
you get a "web page".
If you set a HorizontalLayout
to a defined size
horizontally or a VerticalLayout
vertically,
and there is space left over from the contained components, the extra
space is distributed equally between the component cells. The
components are aligned within these cells according to their
alignment setting, top left by default, as in the example below.
fixedLayout.setWidth("400px");
Using percentual sizes for contained components requires answering the question, "Percentage of what?" There is no sensible default answer for this question in the current implementation of the layouts, so in practice, you may not define "100%" size alone.
Often, you want to have one component that takes all the available
space left over from other components. You need to set its size as
100% and set it as expanding with
setExpandRatio()
. The second parameter for
the method is an expansion ratio, which is relevant if there are more
than one expanding component, but its value is irrelevant for a single
expanding component.
HorizontalLayout layout = new HorizontalLayout(); layout.setWidth("400px"); // These buttons take the minimum size. layout.addComponent(new Button("Component")); layout.addComponent(new Button("Component")); // This button will expand. Button expandButton = new Button("Component"); // Use 100% of the expansion cell's width. expandButton.setWidth("100%"); // The component must be added to layout before setting the ratio. layout.addComponent(expandButton); // Set the component's cell to expand. layout.setExpandRatio(expandButton, 1.0f); parentLayout.addComponent(layout);
Notice that you must call setExpandRatio()
after addComponent()
,
because the layout can not operate on an component that it doesn't
(yet) include.
Warning: A layout that contains components with percentual size must have a defined size! If a layout has undefined size and component has, say, 100% size, the component would fill the space given by the layout, while the layout would shrink to fit the space taken by the component, which is a paradox. This requirement holds for height and width separately. The debug mode allows detecting such invalid cases; see Section 9.1.1, “Debug Mode”.
If you specify an expand ratio for multiple components, they will all try to use the available space according to the ratio.
HorizontalLayout layout = new HorizontalLayout(); layout.setWidth("400px"); // Create three equally expanding components. String[] captions = { "Small", "Medium-sized", "Quite a big component" }; for (int i = 1; i <= 3; i++) { Button button = new Button(captions[i-1]); button.setWidth("100%"); layout.addComponent(button); // Have uniform 1:1:1 expand ratio. layout.setExpandRatio(button, 1.0f); }
As we used the same ratio for each components, the ones with more content may be have the content cut. Below, we use differing ratios:
// Expand ratios for the components are 1:2:3. layout.setExpandRatio(button, i * 1.0f);
If the size of the expanding components is defined as a percentage
(typically "100%"), the ratio is calculated from the
overall space available for the relatively sized
components. For example, if you have a 100 pixels wide layout with two
cells with 1.0 and 4.0 respective expansion ratios, and both the
components in the layout are set as
setWidth("100%")
, the cells will have
respective widths of 20 and 80 pixels, regardless of the minimum size
of the components.
However, if the size of the contained components is undefined or fixed, the expansion ratio is of the excess available space. In this case, it is the excess space that expands, not the components.
for (int i = 1; i <= 3; i++) { // Button with undefined size. Button button = new Button(captions[i - 1]); layout4.addComponent(button); // Expand ratios are 1:2:3. layout4.setExpandRatio(button, i * 1.0f); }
It is not meaningful to combine expanding components with percentually defined size and components with fixed or undefined size. Such combination can lead to a very expected size for the percentually sized components.
A percentual size of a component defines the size of the component within it's cell. Usually, you use "100%", but a smaller percentage or a fixed size (smaller than the cell size) will leave an empty space in the cell and align the component within the cell according to its alignment setting, top left by default.
HorizontalLayout layout50 = new HorizontalLayout(); layout50.setWidth("400px"); String[] captions1 = { "Small 50%", "Medium 50%", "Quite a big 50%" }; for (int i = 1; i <= 3; i++) { Button button = new Button(captions1[i-1]); button.setWidth("50%"); layout50.addComponent(button); // Expand ratios for the components are 1:2:3. layout50.setExpandRatio(button, i * 1.0f); } parentLayout.addComponent(layout50);
GridLayout
container lays components out on a grid
of defined width and height. The columns and rows of the grid serve as
coordinates that are used for laying out components on the grid. Each
component can use multiple cells from the grid, defined as an area
(x1,y1,x2,y2), although they typically take up only a single grid cell.
The grid layout maintains a cursor for adding components in left-to-right, top-to-bottom order. If the cursor goes past the bottom-right corner, it will automatically extend the grid downwards.
The following example demonstrates the use of
GridLayout
. The
addComponent
takes a component and optional
coordinates. The coordinates can be given for a single cell or for an area
in x,y (column,row) order. The coordinate values have a base value of
0. If coordinates are not given, the cursor will be used.
/* Create a 4 by 4 grid layout. */ GridLayout grid = new GridLayout(4, 4); grid.addStyleName("example-gridlayout"); /* Fill out the first row using the cursor. */ grid.addComponent(new Button("R/C 1")); for (int i = 0; i < 3; i++) { grid.addComponent(new Button("Col " + (grid.getCursorX() + 1))); } /* Fill out the first column using coordinates. */ for (int i = 1; i < 4; i++) { grid.addComponent(new Button("Row " + i), 0, i); } /* Add some components of various shapes. */ grid.addComponent(new Button("3x1 button"), 1, 1, 3, 1); grid.addComponent(new Label("1x2 cell"), 1, 2, 1, 3); InlineDateField date = new InlineDateField("A 2x2 date field"); date.setResolution(DateField.RESOLUTION_DAY); grid.addComponent(date, 2, 2, 3, 3);
The resulting layout will look as follows. The borders have been made visible to illustrate the layout cells.
For a more complete example of grid layout, please see Section 1.2.2, “Calculator”.
A component to be placed on the grid must not overlap with existing
components. A conflict causes throwing a
GridLayout.OverlapsException
.
You can define the size of both a grid layout and its components in
either fixed or percentual units, or leave the size undefined
altogether, as described in Section 4.18.1, “Sizing Components through Sizeable
interface”. Section 5.3.1, “Layout Size” gives an introduction to sizing of
layouts.
The size of the GridLayout
component is
undefined by default, so it will shrink to fit the size of the
components placed inside it. In most cases, especially if you set a
defined size for the layout but do not set the contained components to
full size, there will be some unused space. The position of the
non-full components within the grid cells will be determined by their
alignment. See Section 5.3.2, “Layout Cell Alignment” for details on how to align the
components inside the cells.
The components contained within a GridLayout
layout can be laid out in a number of different ways depending on how
you specify their height or width. The layout options are similar to
HorizontalLayout
and
VerticalLayout
, as described in Section 5.2.1, “VerticalLayout
and HorizontalLayout
”.
Warning: A layout that contains components with percentual size must have a defined size! If a layout has undefined size and component has, say, 100% size, the component would fill the space given by the layout, while the layout would shrink to fit the space taken by the component, which is a paradox. This requirement holds for height and width separately. The debug mode allows detecting such invalid cases; see Section 9.1.1, “Debug Mode”.
Often, you want to have one or more rows or columns that take all the
available space left over from non-expanding rows or columns. You need
to set the rows or columns as expanding with
setRowExpandRatio()
and
setColumnExpandRatio()
. The first parameter
for these methods is the index of the row or column to set as
expanding. The second parameter for the methods is an expansion ratio,
which is relevant if there are more than one expanding row or column,
but its value is irrelevant if there is only one. With multiple
expanding rows or columns, the ratio parameter sets the relative
portion how much a specific row/column will take in relation with the
other expanding rows/columns.
GridLayout grid = new GridLayout(3,2); grid.addStyleName("gridexpandratio"); // Layout containing relatively sized components must have a defined size. grid.setWidth("600px"); grid.setHeight("200px"); // Add content grid.addComponent(new Label("Shrinking column<br/>Shrinking row", Label.CONTENT_XHTML)); grid.addComponent(new Label("Expanding column (1:)<br/>Shrinking row", Label.CONTENT_XHTML)); grid.addComponent(new Label("Expanding column (5:)<br/>Shrinking row", Label.CONTENT_XHTML)); grid.addComponent(new Label("Shrinking column<br/>Expanding row", Label.CONTENT_XHTML)); grid.addComponent(new Label("Expanding column (1:)<br/>Expanding row", Label.CONTENT_XHTML)); grid.addComponent(new Label("Expanding column (5:)<br/>Expanding row", Label.CONTENT_XHTML)); // Set different expansion ratios for the two columns grid.setColumnExpandRatio(1, 1); grid.setColumnExpandRatio(2, 5); // Set the bottom row to expand grid.setRowExpandRatio(1, 1); // Align and size the labels. for (int col=0; col<grid.getColumns(); col++) { for (int row=0; row<grid.getRows(); row++) { Component c = grid.getComponent(col, row); grid.setComponentAlignment(c, Alignment.TOP_CENTER); // Make the labels high to illustrate the empty horizontal space. if (col != 0 || row != 0) { c.setHeight("100%"); } } }
If the size of the contained components is undefined or fixed, the
expansion ratio is of the excess space, as in
Figure 5.3, “Expanding Rows and Columns in GridLayout
” (excess
horizontal space shown in white). However, if the size of the all the
contained components in the expanding rows or columns is defined as a
percentage, the ratio is calculated from the
overall space available for the percentually
sized components. For example, if we had a 100 pixels wide grid layout
with two columns with 1.0 and 4.0 respective expansion ratios, and all
the components in the grid were set as
setWidth("100%")
, the columns would have
respective widths of 20 and 80 pixels, regardless of the minimum size
of their contained components.
.i-gridlayout {} .i-gridlayout-margin {}
The i-gridlayout is the root element of the
GridLayout
component. The
i-gridlayout-margin is a simple element inside it
that allows setting a padding between the outer element and the cells.
For styling the individual grid cells, you should style the components
inserted in the cells. The implementation structure of the grid can
change, so depending on it, as is done in the example below, is not
generally recommended. Normally, if you want to have, for example, a
different color for a certain cell, just make set the component inside
it setSizeFull()
, and add a style name for
it. Sometimes you may need to use a layout component between a cell
and its actual component just for styling.
The following example shows how to make the grid borders visible, as
in Figure 5.3, “Expanding Rows and Columns in GridLayout
”.
.i-gridlayout-gridexpandratio { background: blue; /* Creates a "border" around the layout grid. */ margin: 10px; /* Empty space around the layout. */ } /* Add padding through which the background color of the grid shows. */ .i-gridlayout-gridexpandratio .i-gridlayout-margin { padding: 2px; } /* Add cell borders and make the cell backgrounds white. * Warning: This depends heavily on the HTML structure. */ .i-gridlayout-gridexpandratio > div > div > div { padding: 2px; /* Layout's background will show through. */ background: white; /* The cells will be colored white. */ } /* Components inside the layout. This is a safe way to style the cells. */ .i-gridlayout-gridexpandratio .i-label { text-align: left; background: #ffffc0; /* Pale yellow */ }
You should beware of margin
,
padding
, and border
settings in
CSS as they can mess up the layout. The dimensions of layouts are
calculated in the Client-Side Engine of IT Mill Toolkit and some
settings can interfere with these calculations.
Panel is a simple container with a frame and an optional caption. The content are has an inner layout component for laying out the contained components.
The caption can have an icon in addition to the text.
// Create a panel with a caption. final Panel panel = new Panel("Contact Information"); panel.addStyleName("panelexample"); // The width of a Panel is 100% by default, make it // shrink to fit the contents. panel.setWidth(Sizeable.SIZE_UNDEFINED, 0); // Create a layout inside the panel, and have some margin around it. final FormLayout form = new FormLayout(); form.setMargin(true); // Add some components form.addComponent(new TextField("Name")); form.addComponent(new TextField("Email")); // Set the layout as the root layout of the panel panel.setLayout(form);
The resulting layout will look as follows.
.i-panel {} .i-panel-caption {} .i-panel-nocaption {} .i-panel-content {} .i-panel-deco {}
The entire panel has i-panel
style. A panel
consists of three parts: the caption, content, and bottom decorations
(shadow). These can be styled with i-panel-caption
,
i-panel-content
, and
i-panel-deco
, respectively. If the panel has no
caption, the caption element will have the style
i-panel-nocaption
.
The light style for the
Panel
is a predefined style that has now
borders or border decorations for the panel. You enable it simply by
adding the light
style name for the panel, as is
done in the example below.
The light style is typical when using a Panel
as the root layout of a window or some similar layout, as in the
example below.
// Have a window with a SplitPanel. final Window window = new Window("Window with a Light Panel"); window.setWidth("400px"); window.setHeight("200px"); final SplitPanel splitter = new SplitPanel(SplitPanel.ORIENTATION_HORIZONTAL); window.setLayout(splitter); // Create a panel with a caption. final Panel light = new Panel("Light Panel"); light.setSizeFull(); // The "light" style is a predefined style without borders. light.addStyleName("light"); light.addComponent(new Label("The light Panel has no borders.")); light.getLayout().setMargin(true); // The Panel will act as a "caption" of the left panel in SplitPanel. splitter.addComponent(light); splitter.setSplitPosition(250, Sizeable.UNITS_PIXELS); main.addWindow(window);
The TabSheet
is a multicomponent container that
allows switching between the components with "tabs". The tabs are
organized as a tab bar at the top of the tab sheet. Clicking on a tab
opens its contained component in the main display area of the layout.
New tabs can be added simply with the
addComponent()
method, but doing so leaves them
without a caption. You can set the caption with
setTabCaption()
or simply use the
addTab()
method to create tabs and give them a
caption. In addition to a caption, tabs can contain an icon, which you can
define either in the addtab()
call or set later
with setTabIcon()
.
The following example demonstrates the creation of a simple tab sheet, where
each the tabs shows a different Label
component. The tabs have an icon, which are loaded as Java class loader
resources from the WAR package of the application.
final TabSheet tabsheet = new TabSheet(); tabsheet.addStyleName("tabsheetexample"); // Make the tabsheet to shrink to fit the contents. tabsheet.setSizeUndefined(); tabsheet.addTab(new Label("Contents of the first tab"), "First Tab", new ClassResource("images/Mercury_small.png", this)); tabsheet.addTab(new Label("Contents of the second tab"), "Second Tab", new ClassResource("images/Venus_small.png", this)); tabsheet.addTab(new Label("Contents of the third tab"), "Third tab", new ClassResource("images/Earth_small.png", this));
The hideTabs()
method allows hiding the tab bar
entirely. This can be useful in tabbed document interfaces (TDI) when
there is only one tab. An individual tab can be made invisible by making
its component invisible with setVisible(false)
. A
tab can be disabled by disabling its component with
setEnabled(false)
. A tab can be selected
programmatically with setSelectedTab()
.
Clicking on a tab selects it. This fires a
TabSheet.SelectedTabChangeEvent
, which can be
handled with the
TabSheet.SelectedTabChangeListener
. The source
component of the event, which you can retrieve with
getSource()
method of the event, will be the
TabSheet
component. You can find out the currently
selected component with getSelectedTab()
.
The example below demonstrates handling TabSheet
related events and enabling and disabling tabs. The sort of logic used in
the example is useful in sequential user interfaces, often called
wizards, where the user goes through the tabs one by
one, but can return back if needed.
import com.itmill.toolkit.ui.*; import com.itmill.toolkit.ui.Button.ClickEvent; import com.itmill.toolkit.ui.TabSheet.SelectedTabChangeEvent; public class TabSheetExample extends CustomComponent implements Button.ClickListener, TabSheet.SelectedTabChangeListener { TabSheet tabsheet = new TabSheet(); Button tab1 = new Button("Push this button"); Label tab2 = new Label("Contents of Second Tab"); Label tab3 = new Label("Contents of Third Tab"); TabSheetExample () { setCompositionRoot (tabsheet); /* Listen for changes in tab selection. */ tabsheet.addListener(this); /* First tab contains a button, for which we listen button click events. */ tab1.addListener(this); tabsheet.addTab(tab1, "First Tab", null); /* A tab that is initially invisible. */ tab2.setVisible(false); tabsheet.addTab(tab2, "Second Tab", null); /* A tab that is initially disabled. */ tab3.setEnabled(false); tabsheet.addTab(tab3, "Third tab", null); } public void buttonClick(ClickEvent event) { /* Enable the invisible and disabled tabs. */ tab2.setVisible(true); tab3.setEnabled(true); /* Change selection automatically to second tab. */ tabsheet.setSelectedTab(tab2); } public void selectedTabChange(SelectedTabChangeEvent event) { /* Cast to a TabSheet. This isn't really necessary in this example, * as we have only one TabSheet component, but would be useful if * there were multiple TabSheets. */ TabSheet source = (TabSheet) event.getSource(); if (source == tabsheet) { /* If the first tab was selected. */ if (source.getSelectedTab() == tab1) { tab2.setVisible(false); tab3.setEnabled(false); } } } }