The development of user interfaces in Java typically involves dealing with layout managers. This means that components cannot easily be positioned using absolute coordinates and sizes, but the layout manager is responsible for placing the components on the screen based on constraints the developer has defined. While this approach is very flexible and enables a Java application to cope with different platforms, it certainly does not simplify the creation of a good-looking and ergonomic GUI.
From the beginning, the Java development kit offered a number of layout
managers. Most of them are pretty simple and do not support the creation of
a sophisticated user interface out of the box. The idea was to nest multiple
panels with different layout managers to achieve more complex layouts. This
works to a certain degree, but if the UI becomes more complex, the required
code for building the panels and nesting them properly also grows more and
more complex; and if you want to rearrange something later, you are in a
maintenance nightmare! In addition to these simple layout managers, the
java.awt
package provides the GridBagLayout
class, which in principle allows arbitrary complex UIs. However, this
layout manager is very hard to understand - this is probably the reason why
it never becomes pretty popular.
With the
PercentLayout
layout manager the JGUIraffe library tries
to find a compromise: a layout that should not be too difficult to grasp,
but that is powerful enough for complex user interfaces. PercentLayout
is similar to the popular FormLayout
of
jGoodies. It provides
the basic layout functionality implemented within JGUIraffe. On top
of this class there are a couple of other layout managers for special
purposes or frequent use cases. In the following sections the basics of
PercentLayout
are covered. Then we discuss the other layout
managers shipped with JGUIraffe and present a usage example.
Similar to jGoodies' FormLayout
PercentLayout
organizes the UI in a tabular structure. The available space is grouped in
columns and rows. Each cell in this table has certain properties that
determine its size in both the X- and the Y-direction. Each cell can also
host a component (e.g. a label, a checkbox, or a text field). Then its
properties also define the component's size and alignment. For a better
understanding take a look at the following figure:
This picture shows a layout consisting of 9 columns and 9 rows. The
structure of the layout is pretty regular: It contains labels and text
input fields. In most cases the labels are contained in the columns with
the indices 1 and 5, the input fields are located in the columns 3 and 7.
So, from the user's point of view, a UI results consisting of two columns
with input elements. PercentLayout
is well suited for such
regular layouts as it allows defining the properties for the columns and
rows globally. Line 5 is somewhat special: it contains a larger text
area spanning multiple columns, and it is also higher than the other rows.
This demonstrates that single components can be treated in a specific way.
Now, which properties are available for defining a cell in the layout?
When defining a column or a row the following properties can be set (note that these properties are independent for the cell's width and height; so, for instance, a different alignment can be set for the width than for the height):
PercentLayout
evaluates all weight factors and assigns
additional space to cells with a weight factor > 0 in
proportion to these factors. In principle, arbitrary integer numbers can
be used as weight factors. However, it is recommended to use
numbers that sum up to 100. Then the weight factor can be
interpreted as percentage rate of the additional space that is allocated
to a cell. To make this clearer, take another look at the figure with the
example layout. If the user enlarges the window, it makes sense that the
columns containing text fields (i.e. the columns 3 and 7) grow
accordingly. So we can set the weight factors of these columns
to 50. Or maybe the text fields in column 7 are more important than the
ones in column 3? Then we could set the weight factor of column
7 to 67 and the one of column 3 to 33. This means that 67% of additional
space is assigned to column 7, while only 33% is allocated to column 3
(provided that all other weight factors are set to 0). By the
way: The interpretation of the weight factors as percentage rate
is the reason for the name PercentLayout
.In addition to the properties described so far, there is another aspect that influences the size of a cell: the so-called cell groups. A cell group combines multiple columns or rows with each other and ensures that they all have the same width or height. A use case for cell groups are columns with labels. In the example layout shown at the beginning of this section there are two columns with labels: column 1 and column 5. Per default the sizes of these columns would depend on the widths of the labels contained in them. So if column 1 contained only labels with short texts and column 5 contained labels with longer texts, the columns would have different widths. To avoid this a cell group can be defined that combines columns 1 and 5. This guarantees that these columns have the same width, i.e. the maximum width of both columns. The result certainly looks much better than a layout with different column sizes.
When defining a layout, often the preferred size of the components involved is used to determine the size of the layout. When the associated container is initially created the percent layout uses this information to calculate the container's size. This should result in an ideal appearance. We have already explained what happens if additional space becomes available: it is distributed over the layout's cells based on their weight factors. But what happens if the container becomes smaller?
The percent layout manager provides a boolean flag that determines its
behavior in this case: canShrink
. If this flag is set to false
(the default value is true), the cells cannot become smaller than the
size calculated based on the preferred sizes. If the user decreases the
container's size more and more, parts of the UI are cut. This is shown in
the following figure. As can be seen, parts of the lower right become
invisible as the window becomes too small.
Note: There does not seem to be a reliable way in Swing to enforce a minimum size of a window. Even if all components involved have a minimum size set, the window can be dragged smaller - with the effect that parts of the UI become invisible as demonstrated by the figure. For dialog windows that do not benefit from additional size (e.g. because only small input fields for numbers or dates are contained) it may be a good solution to disable sizing at all. However, if the dialog contains text areas, tables, or other scrollable elements, you do not want to do this.
If the canShrink
attribute of a percent layout is set to
true, the layout manager's behavior when calculating the cell sizes
depends on the space currently available. If there is enough space, the
calculation is performed as described above. If the space shrinks below this
threshold, the layout manager acts as if the cell sizes of all cells had
been set to minimum rather than preferred. This
means that this time the minimum size of all components involved is queried.
For components whose minimum size is equal to their preferred size, or for
cells whose size has already been minimum, there is no difference.
But other components and cells may be able to shrink. After determining the
minimum sizes the layout manager checks whether the layout now fits in the
space available or if even more space is available. In this case, the
remaining space is again distributed over the cells with a weight factor.
This approach allows the layout to shrink in a graceful way. As an
example consider the following simple window that only contains a text area.
It has a border layout, and the text area is added to the center.
Because the text area has set the number of columns and rows to be displayed it has a preferred size. After opening the window this size is used. The figure shows the window in its preferred size. Now the window's size is reduced. Because the layout can shrink, the text area component becomes smaller, too, which is indicated by the scroll bars.
When working with layouts that can shrink be careful with the components involved: All components with a preferred size larger than the minimum size should be added to cells with a non-null weight factor. Otherwise, the size of these components may suddenly jump from preffered to minimum when the user drags the window to become smaller. This is probably confusing.
After the concepts of the PercentLayout
manager have been
discussed it is time to have a closer look at the implementation classes.
At first there are some helper classes that need to be introduced.
NumberWithUnit
When dealing with GUI programming the default unit for specifying sizes or
gaps is typically pixels. This is pretty natural, however, the
results of this approach may strongly depend on the users' screen
resolution. This means that a layout may look good on one screen, while it
is too small or too big on another one. To address this problem the
PercentLayout
layout manager supports multiple units for sizes
or gaps.
All numeric values required for a layout definition are specified as
instances of the
NumberWithUnit
class. As the name implies, this class stores a
numeric value (as a floating point number) and an associated unit. It
defines methods for converting this value into pixels, which is required for
internal layout calculations. The supported units are defined by the
Unit
enumeration class. These are the following:
To create an instance of NumberWithUnit
a double for the
actual value and a constant of the Unit
enumaration class have
to be passed to the constructor. There is also a convenience constructor
for pixels, which expects an int. Finally, there is a constructor
that takes a string and parses it to a number with a unit. The string must
start with a floating point number, followed by optional whitespace and the
short name of the unit. Examples for valid strings are 10cm, 10 IN,
10 Dlu, 10px
.
CellConstraints
The
CellConstraints
class is used for defining the properties of a
cell (a column or a row) in the layout. An instance stores exactly the
properties that have been discused in the last section: the cell size
(provided as an instance of the
CellSize
enumeration class), the minimum size of the cell, the
cell alignment (provided as an instance of the
CellAlignment
enumeration class), and a weight factor.
Instances of CellConstraints
are not created directly, but a
builder is used for this purpose. This makes the creation of
instances pretty convenient as only the properties have to be defined that
are needed. The following code fragment shows how the builder is used:
// First create the builder CellConstraints.Builder builder = new CellConstraints.Builder(); // Now create instances CellConstraints cc = builder.withCellSize(CellSize.MINIMUM) .withCellAlignment(CellAlignment.CENTER) .withWeight(50) .create();
Basically the several with()
methods of the builder are called
to define the desired properties. With a call of the create()
method the CellConstraints
instance is actually created.
Instead of setting the properties of a CellConstraints
instance
step by step, an instance can also be defined using a specification
string. This is a string that defines all possible properties in a
compact form. The syntax is as follows:
<cell alignment>/<cell size>(<minimum size>)/<weight>
The alignment, the cell size, and the weight factor can be defined separated by slash ("/") characters. The alignment and the weight factor are optional, only the cell size must be defined. The strings are not case sensitive. The following strings are all valid specification strings:
CellConstraints
has a fromString()
method that returns an instance for a valid specification string. For more
options of
CellConstraints
have a look at the Javadocs of this class.
PercentData
After a layout has been created and configured the components to be managed
have to be added. More complex layout managers require constraints objects
that exactly specify how a specific component should be handled. For
instance, the well-known BorderLayout
layout manager from the
java.awt
package needs a name of a cell, in which the component
is to be added (e.g. NORTH or CENTER), as constraints. The
java.awt.GridBagLayout
layout manager uses the
GridBagConstraints
class for this purpose.
PercentLayout
also defines its own class for layout constraints:
PercentData
.
A PercentData
object has the following properties:
PercentData
allows defining the number of cells spanned by
the component in X and Y direction.PercentLayout
supports setting
special CellConstraints
objects for both the column and the
row, which override the default settings. Using this feature special
properties can be set for specific components.
To create new PercentData
objects an analogous approach is used
as for CellConstraints
: There is a Builder
class
which allows setting of properties in a convenient way. A typical sequence
for creating PercentData
objects using the builder could look
as follows:
// First create the builder PercentData.Builder b = new PercentData.Builder(); // Now create instances PercentData pd1 = b.xy(1, 2).create(); PercentData pd2 = b.xy(1, 3).spanX(2).withColumnConstraints(columnConstr).create();
The Builder
class defines a bunch of methods that correspond to
the properties supported by PercentData
. The methods
withColumnConstraints()
, withRowConstraints()
,
withTargetColumn()
, and withTargetRow()
should
be self-explanatory. With the xy()
method the indices of the
column and the row the component is to be placed in are specified. The
number of columns or rows spanned by the component can be set either
separately using the spanX()
and spanY()
methods
or in a single operation using the span()
method. Often a
PercentData
object will only be initialized with the column and
row indices - these are the only mandatory properties. For this special use
case the Builder
class defines the convenience method
pos()
. pos()
sets the indices and creates the new
instance immideately. So the create()
method need not be called.
CellGroup
The concept of cell groups has already been introduced. Cell groups
allow the definition of columns or rows that should always have the same
width or height. The corresponding API of PercentLayout
makes
use of the
CellGroup
class.
CellGroup
is a pretty simple class. It basically manages a
number of (0-based) indices for the columns or rows that belong to the
group. Typically cell groups do not have too many elements. Therefore there
are convenience constructors that accept 2, 3, and 4 indices - this should
cover the most frequent use cases. If this is not enough, there is the
static fromArray()
method, which reads the indices of the
group from an arbitrary array.
The static fromString()
method allows initializing a
CellGroup
from a string representation. Valid strings contain
the numeric indices separated by various supported separater characters,
for instance:
1,3,4
4 3 1
1, 2;3 / 4
CellGroup
objects are created and passed
to a PercentLayout
object.
The implementation of the PercentLayout
manager is somewhat
special because it does not implement a platform-specific layout manager
interface. For instance, when working with Swing all layout managers must
implement the java.awt.LayoutManager2
interface. The
implementation classes of PercentLayout
do not do this.
Rather, they implement only the layout algorithm in a way that is independent
on a concrete GUI library. For accessing platform-specific functionality
(e.g. setting or obtaining the size of a component) an adapter is used. The
functionality of this adapter is defined by the
PercentLayoutPlatformAdapter
interface.
The advantage of this approach is that the PercentLayout
manager can easily be ported to different UI libraries. The base classes
can remain unchanged; only a specialized adapter has to be written. For
Swing there is already a fully functional adapter implementation:
SwingPercentLayoutAdapter
. SwingPercentLayoutAdapter
also implements the java.awt.LayoutManager2
interface. When an
instance is constructed it is passed the PercentLayout
object
it is associated with. Then it can be installed as a regular Swing layout
manager, for instance:
JPanel panel = new JPanel(); PercentLayoutBase percentLayout = ... SwingPercentLayoutAdapter adapter = new SwingPercentLayoutAdapter(percentLayout); panel.setLayout(adapter);
The layout functionality discussed so far is implemented by the
PercentLayout
class. To install a layout of this type an instance
of PercentLayout
has to be created and configured. Then a
platform-specific adapter has to be associated with it as shown in the code
fragment above.
PercentLayout
provides three different constructors:
CellConstraints
objects. These collections define the
properties of the columns and the rows of the layout. The resulting layout
has as many columns and rows as these collections contain elements.CellConstraints
objects defining the
layout's columns and rows are specified as strings conforming to the
format already discussed in this document. The first string contains an
arbitrary number of CellConstraints
specification strings for
the columns of the layout separated by "," characters, the
second string is analogous, but it defines the constraints for the layout's
rows. This is a pretty compact way of defining the layout:
PercentLayout layout = new PercentLayout( "3dlu, end/preferred, 3dlu, full/preferred(1in)/50, 7dlu, end/preferred, " + "3dlu, full/preferred(1in)/50, 3dlu", "3dlu, preferred, 3dlu, preferred, 3dlu, preferred, 7dlu, preferred, " + 3dlu, preferred, 3dlu, full/preferred(1in)/100, 3dlu");
setColumnConstraints()
and
setRowConstraints()
which allow assigning a contraints object
to a single column or row. This constructor is mainly intended to be used
by sub classes.
After an instance of PercentLayout
was created further
configuration can be performed:
CellConstraints
objects for columns or rows can be set using the
setColumnConstraints()
and setRowConstraints()
methods.CellGroup
objects (see above) can be added using the
addColumnGroup()
and addRowGroup()
methods.
This determines the columns or rows which should have the same size.
After the configuration of the PercentLayout
object, and with
the PercentLayoutPlatformAdapter
in place the layout can be
used as any regular layout manager instance. This means that components can
be added to a container using PercentData
objects as layout
constraints. We will show this later in the examples section.
PercentLayout
is the most generic and powerful layout
implementation shipped with the JGUIraffe library. There are a
couple of other layout manager implementations on top of
PercentLayout
for special use cases. One of these is
BorderLayout
BorderLayout
is very similar to the layout manager with the
same name from the standard java.awt
package. (More information
about java.awt.BorderLayout
can be found for instance in the
Swing Tutorial.) It provides some additional functionality, e.g. the
definition of margins and gaps between the components it manages. And, it is
also written in a platform-independent way; so it can easily be ported to
other GUI libraries which do not support this type of layout out of the box.
Using BorderLayout
is simple. After creating a
BorderLayout
instance (which does not require any parameters)
some configuration properties can be set using the following methods:
NumberWithUnit
object. So the margins and gaps can be specified
in any of the supported units. The default is 0 for all margins and gaps
(which conforms to the behavior of the classic java.awt.BorderLayout
).
BorderLayout
also needs a PercentLayoutPlatformAdpater
instance. If this adapter has been installed, components can be added to the
associated container in the usual way using the CENTER, NORTH, WEST,
EAST
or SOUTH
constants of BorderLayout
as
constraints. These constraints define, in which of the managed cells the
component is to be placed. We will see BorderLayout
in action
in the examples section.
Dialog boxes often have a bar with buttons at their botton defining the
actions the user can invoke, e.g. Save, Cancel, or
Help. Given the frequency of this use case, it is surprisingly
difficult to create a corresponding layout using Java board means. A bunch
of code is required to ensure that the buttons in the button bar are properly
aligned and all have the same width. The
ButtonLayout
class is focused on this use case.
A ButtonLayout
object can be instantiated using its default
constructor. Then the following properties can be configured:
setLeftMargin()
, setTopMargin()
,
setRightMargin()
, and setBottomMargin()
. Again,
the NumberWithUnit
class is used for defining the margins.setGap()
method. The gap is also specified using a
NumberWithUnit
object.ButtonLayout.Alignment
enumeration class can be passed to the
setAlignment()
method. The alignment determines whether the
buttons should be aligned to the left, the right, or the center of the
whole dialog.
As for the other layout classes covered so far a
PercentLayoutPlatformAdapter
is required to embed the layout
into a specific GUI platform. Then buttons can be created and added to the
container representing the button bar without passing additional
constraints objects. They will be automatically added to the layout and
placed accordingly. This is shown in detail in the examples section.
In this section a dialog box using the layout classes discussed in this document is developed. We only focus on the code required to construct the UI; no additional functionality (e.g. for the handling of button clicks) is added. As GUI library we will set on Swing. The following figure shows the final dialog box:
The example layout contains two (logic) columns with labels and input fields in the upper part. All labels have the same length. This is also true for the input fields. In the lower part there are longer input fields. Their labels, however, are aligned with the first labels of the upper part. There are also headings with a larger width.
As a general implementation strategy we will use a BorderLayout
that divides the dialog box in a section with the actual input fields
(this is a panel which is added to the CENTER) and a section with
the buttons (another panel in the SOUTH). All components in the
CENTER area can be arranged using a single PercentLayout
object. For the buttons of course ButtonLayout
is the
appropriate choice. The following code shows how the BorderLayout
is created and installed at the content pane of the dialog box:
// The layout of the content pane BorderLayout borderLayout = new BorderLayout(); getContentPane().setLayout(new SwingPercentLayoutAdapter(borderLayout));
As the BorderLayout
needs no special configuration, this code
is very simple. We could have also set some of the margins, but in this
example we use a properly setup PercentLayout
to define the
margins of the dialog box.
The main area of the dialog box that contains all labels and input elements
is realized using PercentLayout
. When working with
PercentLayout
at first the UI has to be devided into columns
and rows; then the properties for these columns and rows have to be defined.
For our example layout we use 9 columns for the following elements:
The constraints for the layout's rows are less complicated because there are only two types of rows: rows containing components (labels and/or input fields) whose height is derived from their content and rows with a minimum height serving as separators between the other rows. All rows with an even row index (starting at 0) are separator rows. An exception is the row containing the text area. Here also a minimum height is defined so that the text area has a certain initial size. Additionally we specify that additional space in the Y direction should be fully consumed by this row because the text area is the only component in this dialog for which growing in the height makes sense. This is achieved by setting the weight factor of this row to 100. To actually create the layout we use string representations for the constraints we thought about in the last paragraphs. This looks as follows:
// setup the main panel and its layout JPanel pnlMain = new JPanel(); PercentLayout layout = new PercentLayout( "3dlu, end/preferred, 3dlu, full/preferred(1in)/50, 7dlu, end/preferred, " + "3dlu, full/preferred(1in)/50, 3dlu", "3dlu, preferred, 3dlu, preferred, 3dlu, preferred, 7dlu, preferred, " + 3dlu, preferred, 3dlu, full/preferred(1in)/100, 3dlu");
The first string parameter of the constructor defines all columns, the second
one defines the rows. As units for margins and spaces we use dialog units.
The meaning of the single definition strings should be clear after reading
the section about cell constraints. With this constructor all constraints of
the layout are specified. What remains is the requirement that some columns
should have the same width: the columns with the labels and the columns with
the text fields. To achieve this some CellGroup
objects are
added to the layot.
layout.addColumnGroup(new CellGroup(1, 5)); layout.addColumnGroup(new CellGroup(3, 7)); pnlMain.setLayout(new SwingPercentLayoutAdapter(layout));
These lines add two CellGroup
objects for columns. One for the
columns with the labels and one for the columns with the text fields. The
last line installs the layout in the panel by making use of a Swing-specific
layout adapter.
Now it is time for adding components to the layout. First some builder objects for creating helper objects are setup. Then the first header line is defined:
// Builder for creating constraints objects CellConstraints.Builder cb = layout.getConstraintsBuilder(); PercentData.Builder pcb = new PercentData.Builder(); // Fill a header line pnlMain.add( new JLabel("General information"), pcb.xy(1, 1).spanX(7).withColumnConstraints(cb.defaultColumn().create()) .create());
Each PercentLayout
object has its own builder for
CellConstraints
objects. This builder can also be used by
client code. We do this here. The builder for PercentData
instances has to be created manually.
The header line consists of a single label. What makes this line special is
the fact that the label spans multiple columns - the X span in the
PercentData
object is set to 7. We also use a specific
CellConstraints
object. The default constraints for column 1
state that components should be right-aligned. The label acting as header is
left-aligned. Now the two rows with labels and text fields are constructed:
// Fill the first data line pnlMain.add(new JLabel("Name:"), pcb.pos(1, 3)); pnlMain.add(new JTextField(), pcb.pos(3, 3)); pnlMain.add(new JLabel("Firstname:"), pcb.pos(5, 3)); pnlMain.add(new JTextField(), pcb.pos(7, 3)); // Fill the second data line pnlMain.add(new JLabel("Street:"), pcb.pos(1, 5)); pnlMain.add(new JTextField(), pcb.pos(3, 5)); pnlMain.add(new JLabel("City:"), pcb.pos(5, 5)); pnlMain.add(new JTextField(), pcb.pos(7, 5));
This is pretty straight-forward: The Swing components for the labels and
text fields are created and placed into the layout. The PercentData
objects used as constraints only define the position in the tabular layout.
Because no other properties need to be set the pos()
convenience method of the PercentData.Builder
class can be
used for this purpose.
The lower half of the dialog box is a bit more irregular. There is again a header line which is analogous to the first one. In the following lines the input fields span multiple columns. The line with the text area is heigher than the other rows. Let's have a look at the code:
// Fill another header line pnlMain.add( new JLabel("Specifics"), pcb.xy(1, 7).spanX(7).withColumnConstraints(cb.defaultColumn().create()) .create()); // Fill third data line pnlMain.add(new JLabel("Email:"), pcb.pos(1, 9)); pnlMain.add(new JTextField(), pcb.xy(3, 9).spanX(5).create()); // Fill fourth data line pnlMain .add( new JLabel("Remarks:"), pcb.xy(1, 11).withRowConstraints( cb.withCellAlignment(CellAlignment.START) .withCellSize(CellSize.PREFERRED) .create()) .create()); pnlMain.add(new JScrollPane(new JTextArea()), pcb.xy(3, 11).spanX(5).create());
The header line and the line with the first text field contain nothing new.
They define an X span so they cover multiple columns. The row with the text
area is a bit more complicated. First, when the layout was constructed a
minimum height was defined for this row by specifying the following
constraints string: full/preferred(1in)/100
. This means that
this row has a height of at least 1 inch, which guarantees an appropriate
size of the text area. Therefore when adding the text area no special
constraints have to be provided except for the column span. For the label
however, we have to override the global row constraints. Here the alignment
is set to CENTER, which would cause the label to be centered
relative to the text area. This would look strange; so we set the alignment
in the row constraints to START. This causes the label to be
top-aligned. Now the main area of the layout with all input components is
complete. What is missing is the button bar:
// The button bar JPanel pnlButtons = new JPanel(); ButtonLayout buttonLayout = new ButtonLayout(); buttonLayout.setGap(new NumberWithUnit(4, Unit.DLU)); buttonLayout.setLeftMargin(new NumberWithUnit(3, Unit.DLU)); buttonLayout.setRightMargin(new NumberWithUnit(3, Unit.DLU)); pnlButtons.setLayout(new SwingPercentLayoutAdapter(buttonLayout)); pnlButtons.add(new JButton("OK")); pnlButtons.add(new JButton("Cancel")); pnlButtons.add(new JButton("Help"));
For the button bar a new panel is created. The layout manager is set to a
ButtonLayout
instance (using a SwingPercentLayoutAdapter
as mediator). Some properties of the ButtonLayout
are set,
then the buttons are added. That's all. Finally the panels for the main
area and the button bar have to be added to the dialog's content pane. It
has to be ensured that the appropriate cells of the BorderLyout
are used:
getContentPane().add(pnlMain, BorderLayout.CENTER); getContentPane().add(pnlButtons, BorderLayout.SOUTH);
This was all the code for constructing the UI. For a better overview the
whole code of the demo application is presented below. The code lines we
just discussed can be found in the init()
method.
package net.sf.jguiraffe.examples.gui.layout; import java.awt.EventQueue; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import net.sf.jguiraffe.gui.layout.BorderLayout; import net.sf.jguiraffe.gui.layout.ButtonLayout; import net.sf.jguiraffe.gui.layout.CellAlignment; import net.sf.jguiraffe.gui.layout.CellConstraints; import net.sf.jguiraffe.gui.layout.CellGroup; import net.sf.jguiraffe.gui.layout.CellSize; import net.sf.jguiraffe.gui.layout.NumberWithUnit; import net.sf.jguiraffe.gui.layout.PercentData; import net.sf.jguiraffe.gui.layout.PercentLayout; import net.sf.jguiraffe.gui.layout.Unit; import net.sf.jguiraffe.gui.platform.swing.layout.SwingPercentLayoutAdapter; /** * An example of using PercentLayout with Swing. */ @SuppressWarnings("serial") public class SwingPercentLayoutExample extends JFrame { public SwingPercentLayoutExample() { super(); init(); pack(); } /** * Constructs the GUI of this frame. */ protected void init() { // init frame setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setTitle("PercentLayout demo"); // The layout of the content pane BorderLayout borderLayout = new BorderLayout(); getContentPane().setLayout(new SwingPercentLayoutAdapter(borderLayout)); // setup the main panel and its layout JPanel pnlMain = new JPanel(); PercentLayout layout = new PercentLayout( "3dlu, end/preferred, 3dlu, full/preferred(1in)/50, 7dlu, " + "end/preferred, 3dlu, full/preferred(1in)/50, 3dlu", "3dlu, preferred, 3dlu, preferred, 3dlu, preferred, 7dlu, " + "preferred, 3dlu, preferred, 3dlu, full/preferred(1in)/100, 3dlu"); layout.addColumnGroup(new CellGroup(1, 5)); layout.addColumnGroup(new CellGroup(3, 7)); pnlMain.setLayout(new SwingPercentLayoutAdapter(layout)); // Builder for creating constraints objects CellConstraints.Builder cb = layout.getConstraintsBuilder(); PercentData.Builder pcb = new PercentData.Builder(); // Fill a header line pnlMain.add(new JLabel("General information"), pcb.xy(1, 1).spanX(7) .withColumnConstraints(cb.defaultColumn().create()).create()); // Fill the first data line pnlMain.add(new JLabel("Name:"), pcb.pos(1, 3)); pnlMain.add(new JTextField(), pcb.pos(3, 3)); pnlMain.add(new JLabel("Firstname:"), pcb.pos(5, 3)); pnlMain.add(new JTextField(), pcb.pos(7, 3)); // Fill the second data line pnlMain.add(new JLabel("Street:"), pcb.pos(1, 5)); pnlMain.add(new JTextField(), pcb.pos(3, 5)); pnlMain.add(new JLabel("City:"), pcb.pos(5, 5)); pnlMain.add(new JTextField(), pcb.pos(7, 5)); // Fill another header line pnlMain.add(new JLabel("Specifics"), pcb.xy(1, 7).spanX(7) .withColumnConstraints(cb.defaultColumn().create()).create()); // Fill third data line pnlMain.add(new JLabel("Email:"), pcb.pos(1, 9)); pnlMain.add(new JTextField(), pcb.xy(3, 9).spanX(5).create()); // Fill fourth data line pnlMain.add(new JLabel("Remarks:"), pcb.xy(1, 11).withRowConstraints( cb.withCellAlignment(CellAlignment.START).withCellSize( CellSize.PREFERRED).create()).create()); pnlMain.add(new JScrollPane(new JTextArea()), pcb.xy(3, 11).spanX(5) .create()); // The button bar JPanel pnlButtons = new JPanel(); ButtonLayout buttonLayout = new ButtonLayout(); buttonLayout.setGap(new NumberWithUnit(4, Unit.DLU)); buttonLayout.setLeftMargin(new NumberWithUnit(3, Unit.DLU)); buttonLayout.setRightMargin(new NumberWithUnit(3, Unit.DLU)); pnlButtons.setLayout(new SwingPercentLayoutAdapter(buttonLayout)); pnlButtons.add(new JButton("OK")); pnlButtons.add(new JButton("Cancel")); pnlButtons.add(new JButton("Help")); getContentPane().add(pnlMain, BorderLayout.CENTER); getContentPane().add(pnlButtons, BorderLayout.SOUTH); } public static void main(String[] args) { final SwingPercentLayoutExample exFrame = new SwingPercentLayoutExample(); EventQueue.invokeLater(new Runnable() { public void run() { exFrame.setVisible(true); } }); } }