Building user interfaces

In the previous sections it was discussed that builders that interprete XML-based scripts play an important role in the JGUIraffe framework. The chapter about the dependency injection builder showed how arbitrary Java objects can be defined and created. In this section we focus on the creation of user interface elements that can be used in forms to gather user input. For this purpose the JGUIraffe library provides a specialized tag library: the form builder or component builder tag library.

The tags of the form builder tag library support the definition of typical UI elements like panels, labels, text fields, or radio buttons. They also allow the creation of layouts for containers. Last, but not least UI-related helper objects like validators or transformers can be declared. One big advantage of the form builder tag library is that it does not only create the user interface, but automatically generates a Form object which is able to process the user input entered in this UI. Refer to the Working with forms section for more information about forms.

We will show how this looks like in practice. But first we have to introduce some central classes and interfaces whose knowledge is important when using the form builder tag library.

Fundamental classes and interfaces

While processing a builder script with UI definitions the builder actually creates two different structures in memory:

  • the actual UI components, i.e. panels, input fields, etc. nested in a hierarchical structure according to the defined layout
  • objects for managing the data of the input components that belong to the UI. These objects allow automatic processing of data entered by the user.

The most important objects for managing input components are ComponentHandler objects. A ComponentHandler wraps a graphical input element and provides read and write access to its data. The type of the data depends on the concrete input element. A text field for instance, has the entered text as its data. For a check box the data consists of a boolean flag (whether the check box is checked or not). For a list box it is the index of the selected element (or an array of the selected indices if multi-selection is allowed). The actual Java data type of a ComponentHandler can be queried using its getType() method.

The methods getData() and setData() can be used to query or set the current content of an input element. Note that the ComponentHandler interface has a generics parameter that defines the return or parameter types of these methods. With the methods isEnabled() and setEnabled() the enabled state of an input element can be queried or set. By using the setEnabled() method it is possible for instance to disable an input element if it is temporarily not available, maybe because of the status of other input elements.

Component handlers are used under the hood by the Form class to transform data between graphical input elements and the form's model or vice versa. So if only the data entered by a user into a form is of interest, a developer need not deal with these handlers directly. However, if the form is more dynamic and should change the status of some components based on user input, it might be necessary to use component handlers to read the current data of input elements and to manipulate other dependent elements. An example is a check box that controls other input elements: only if the check box is checked, the dependent input elements should be enabled. A way to achieve this is to add an event listener at the check box for change events (we will show how this is done in later sections). In this event handler the ComponentHandler for the check box is obtained and queried for the data of the checkbox. This results in a boolean flag which determines the enabled status of the dependent elements.

The ComponentHandler interface defines the minimum protocol required by the framework for the interaction with input components. It does not cover any specialities of components. This is a shortcoming, especially when dealing with more complex input elements like tables or trees. Such components provide special functionality which cannot be accessed by the ComponentHandler interface. Therefore the JGUIraffe library provides a number of specialized interfaces derived from ComponentHandler which are tailored for specific component types. These interfaces live in the net.sf.jguiraffe.gui.builder.components.model package. For instance, there are specialized handler interfaces for tables, trees, or progress bars. If you work with one of these components, the associated ComponentHandler object can be casted to the specialized interface to obtain extended access to this component.

While the ComponentHandler interface focuses on data access to input components, the WidgetHandler interface allows the manipulation of graphical properties of elements, e.g. the colors or the visible state. Widget handlers are available for all graphical elements, not only for input elements. This includes labels for example. Again, widget handlers are useful for dynamic forms which adapt their appearance to current user input.

We have seen that during a builder operation a bunch of objects is created: a Form object, component handlers, widget handlers. But how can these objects be accessed? All objects created during the builder operation are collected by a central object which represents the results of the builder. This is an object of type ComponentBuilderData. This class defines a bunch of useful methods for obtaining the objects created:

  • getComponentHandler() returns the ComponentHandler for the input element with the specified name.
  • getComponent() returns the actual component with the specified name. This is the platform-specific UI element, e.g. a Swing text field. Because the type of this element depends on the underlying UI toolkit getComponent() has the unspecific type java.lang.Object as its return type.
  • getWidgetHandler() returns the WidgetHandler for the UI element with the specified name. This method can also be called for non-input elements, provided that a name was specified for them.
  • getForm() returns a reference to the Form object created during the builder operation. This form contains fields for all the input elements defined in the builder script.
  • getBeanContext() returns the BeanContext of the current builder operation. This BeanContext provides access to almost all objects created by the builder. Refer to the section The dependency injection builder for more information about bean contexts and the definition of objects. There are some special short cuts for accessing graphical objects produced by the builder like components or their handlers. The Javadocs for the ComponentBuilderData class describe which additional objects can be retrieved in detail.

So from the ComponentBuilderData object access to all builder results is possible. How to obtain the ComponentBuilderData object itself depends on the target object: A class derived from FormController (refer to the section Form controllers) has direct access to the ComponentBuilderData object through its getComponentBuilderData() method. Typically the controller of a form needs this object if it has to manipulate UI elements depending on some logic. If some other object needs access to the ComponentBuilderData object, the best way to achieve this is to define this object in the builder script and inject a corresponding reference. As an example consider the following class definition:

package com.mypackage;

import net.sf.jguiraffe.gui.builder.components.ComponentBuilderData;

public class MyClass
{
    private final ComponentBuilderData builderData;

    public MyClass(ComponentBuilderData cd)
    {
        builderData = cd;
     }

     // remaining part omitted
}
    

Here a simple class is defined which expects a ComponentBuilderData object to be passed to its constructor. An instance of this class can be created in a builder script using the following tags:

<di:bean name="myClassInstance" beanClass="com.mypackage.MyClass">
  <di:constructor>
    <di:param refName="COMPONENT_BUILDER_DATA"/>
  </di:constructor>
</di:bean>
    

Now that we know how to obtain a ComponentBuilderData object we can implement the example mentioned above: a form contains a check box which controls a text field. Only if the check box is checked, the text field should be enabled. We assume that this rule is to be implemented by a FormController object which is registered as change listener at the checkbox. Then the controller class can contain the following code:

/** Constant for the name of the check box component. */
private static final String COMP_CHECKBOX = "testCheckBox";

/** Constant for the name of the text field component. */
private static final String COMP_TEXT = "dependentTextField";

/**
 * Notifies this object about a change in the data of an element.
 * @param e the change event
 */
public void elementChanged(FormChangeEvent e)
{
    // obtain the handler for the check box (the type-safety warning can be
    // suppressed because we know that it is a check box handler
    @SuppressWarnings("unchecked")
    ComponentHandler<Boolean> checkHandler = (ComponentHandler<Boolean>)
        getComponentBuilderData().getComponentHandler(COMP_CHECKBOX);

    // obtain the handler for the text field
    ComponentHandler<?> textHandler =
        getComponentBuilderData.getComponentHandler(COMP_TEXT);

    // Change enabled state based on the check box's data
    Boolean checked = checkHandler.getData();
    textHandler.setEnabled(checked.booleanValue());
}
    

This example shows how UI-related logic can be implemented with component handlers and the help of the ComponentBuilderData object. Event listeners will be discussed in a later section. Next we talk about builder scripts that define UI components.

Builder scripts and base tag handler classes

The previous sections already contained examples for builder scripts. Scripts defining UI components do not look differently. It is essential that the namespace declaration on the root element includes the component builder tag library, so that all these tags can be used. The following listing shows the skeleton of a builder script which we use in the examples in this section. It defines a dialog window with its content:

<?xml version="1.0" encoding="ISO-8859-1"?>
<j:jelly xmlns:j="jelly:core" xmlns:di="diBuilder" xmlns:f="formBuilder"
  xmlns:a="actionBuilder" xmlns:w="windowBuilder">

  <!-- Definition of the dialog window -->
  <w:dialog titleres="dialog_title" center="true">
    <!-- Define the content of the dialog window here -->

  </w:dialog>
</j:jelly>
    

Here the component builder tag library is registered with the namespace prefix "f" (for form). So all XML elements starting with f: are looked up in this tag library. The <j:jelly> root element also declares namespaces for other tag libraries:

  • the "di" namespace for the dependency injection tag library
  • the "a" namespace for the action builder tag library
  • the "w" namespace for the window builder tag library
The dependency injection tag library was already discussed in the previous chapter. The action builder and the window builder tag libraries are subject of following chapters. Typically you want to include all of these tag libraries so that you can use the full functionality provided by JGUIraffe.

The skeleton script contains a <w:dialog> tag which is part of the window builder tag library. This tag will be introduced when the window builder tag library is described. For now it is sufficient to know that this tag - not surprisingly - creates a dialog window. In the body of the tag the definition of the dialog's content is placed. Here the tags of the component builder tag library occur; they define the UI elements that comprise the UI of the dialog window.

The component builder tag library contains a large number of tags for creating various UI components and objects related to them. There are some exceptions, but most of the tags can be grouped into one of these categories:

  • graphical input elements
  • graphical elements which do not gather user input
  • non-graphical objects
Technically, abstract base classes are used to implement the tag handler classes for the tags in the different categories. While the developer usually does not have to deal with these base classes, it makes sense to have a short glance at them because they do not only implement base functionality, but also provide common attributes for all derived tag handler classes. We will give a short overview over these base tag handler classes and the attributes they support.

The most tags contained in the component builder tag library are derived (either directly or indirectly) from the FormBaseTag class. This class does not implement a great deal of functionality, it mainly provides access to the current ComponentBuilderData object which lives in the Jelly context. But it defines two interesting attributes which are inherited by all sub classes: ifName and unlessName. These attributes control a conditional execution of the tag.

The builder that processes the builder scripts can be assigned a name. The ifName and unlessName attributes compare the name of the builder with the value of the attribute and execute the tag only if the names match (in the case of ifName) or do not match (for unlessName). The background for these attributes lies in the fact that JGUIraffe tries to be compatible with multiple UI libraries. It may be possible that some libraries require specific adaptations in builder scripts. Using these attributes it is pretty easy to implement such adaptions by letting certain tags only execute for specific builders. For instance, some parts of the builder script should only be executed if Swing is used as underlying UI library; other parts may deal with specialities of SWT, etc. Conditional execution of tags is of course also possible with tags of the standard Jelly tag libraries. However, the ifName and unlessName attributes provide a convenient short cut.

The next abstract base class in the hierarchy of the component builder tag library is ComponentBaseTag. It acts as the base class for all graphical elements and defines a set of common standard attributes. With these attributes the following properties of graphical components can be set:

  • A name for the component. Names are optional. They identify objects in the builder script in a unique way and are needed if the elements are to be accessed programmatically. They are also used to set the name of the underlying UI control which is required for accessible user interfaces.
  • The foreground and the background color. Colors are specified as strings that are parsed by the ColorHelper helper class. Have a look at the documentation of this class to learn about the format of strings describing colors.
  • The font of the component.
  • An object with layout constraints. Such objects tell the layout manager how to place the corresponding component on the screen. We talk about the definition of layouts later in this section.

From ComponentBaseTag two other base classes are derived: SimpleComponentTag and InputComponentTag. SimpleComponentTag is the base class for graphical elements that do not support user input. No additional standard attributes are defined. For instance, the tag for defining a label is derived from this class. Container tags are also sub classes of SimpleComponentTag. Container tags are tags that manage a set of other components, e.g. a panel.

Many tags of the component builder tag library define input elements and thus are derived from InputComponentTag. This base class not only takes care about the creation of the corresponding input element, but also ensures that appropriate handler objects are created and added to the current Form object. This establishes a connection between the input element and the model of the form, so that the form can be used to read and write the data of the input element. For input elements a bunch of standard attributes is available (in addition to the ones supported for all components):

  • The name of the input element is now mandatory. It is used to associate the input element with a field of the form.
  • An optional display name can be specified. This name is displayed to the user, e.g. in case of a validation error. It can be specified directly or as a resource name.
  • An optional property name can be set. This is the name of the corresponding property in the form's model object. If it is not specified, the name of the input element is used.
  • In some use cases input elements should be placed on the UI which are not associated with a form. For instance, the element may be used to disable or enable other parts of the UI, but it should not be saved with the other data entered by the user. In this case the noField attribute can be set to true. This attribute prevents that a field is created for the input element in the current form.
  • Using the groups attribute an input element can be added to one or more logic groups of components. This is just a logic grouping and not visible. Component groups provide some functionality, e.g. to obtain ComponentHandler objects for all elements in the group or to change the enabled state of all elements.

The last base class we want to discuss here is UseBeanBaseTag. The standard tag libraries shipped with Jelly provide a means for creating and accessing beans: the <useBean> tag. Using this tag simple Java objects can be created by calling their standard constructor, and attributes of the tag are used to define properties. The UseBeanBaseTag class of the component builder tag library extends this functionality to better integrate with the dependency injection framework. The main purpose of tags derived from UseBeanBaseTag is to fetch a bean that was defined by a <di:bean> tag and to pass it to another tag handler. For instance, each input component can be assigned a validator object. The <f:validator> tag is responsible for this task. It is derived from UseBeanBaseTag. So it can either create a validator object directly or obtain one that was defined using a <di:bean> tag. This object is then passed to the target input element.

A simple example

To give you a better impression of how the definition of a user interface in a builder script looks like we present parts of a script that defines a simple dialog box. Using this script we can explain some of the essentials of this approach. The following figure shows the dialog that is to be defined:

Example dialog

This is a simple dialog that allows the user to create a new file. The name of the file and its content can be specified in input fields. The layout is not very complex: the main part of the dialog consists of the two text input fields and their labels; at the bottom there is a bar with the buttons for closing or canceling the dialog. The builder script that defines the dialog is presented below (we only focus on the UI part; some definitions for controller elements are omitted):

<j:jelly xmlns:j="jelly:core" xmlns:di="diBuilder" xmlns:f="formBuilder"
  xmlns:a="actionBuilder" xmlns:w="windowBuilder">

  <!-- A validator for required input fields.-->
  <di:bean name="requiredValidator"
    beanClass="net.sf.jguiraffe.transform.RequiredValidator">
  </di:bean>

  <!-- A regular expression validator for validating a file name. Certain
       characters are not allowed in file names.
  -->
  <di:bean name="fileNameValidator"
    beanClass="net.sf.jguiraffe.transform.RegexValidator">
    <di:setProperty property="regex" value="[^\*\\/~\|&lt;&gt;]*"/>
  </di:bean>

  <!-- The dialog window -->
  <w:dialog titleres="newfile_title" center="true" resizable="true">
    <f:borderlayout/>
    <!-- The main panel -->
    <f:panel>
      <f:borderconstr name="CENTER"/>
      <f:percentlayout columns="4dlu start/preferred 3dlu full/preferred(6cm)/100 4dlu"
        rows="4dlu preferred 3dlu preferred full/preferred(5cm)/100 4dlu"/>
      <f:label textres="newfile_lab_name">
        <f:percentconstr col="1" row="1"/>
      </f:label>
      <f:textfield name="fileName" displayNameres="newfile_disp_name"
        maxlength="200" tooltipres="newfile_txt_name_tip">
        <f:percentconstr col="3" row="1"/>
        <f:validators phase="syntax">
          <f:validator beanName="requiredValidator"/>
          <f:validator beanName="fileNameValidator">
            <f:properties>
              <f:property property="ERR_PATTERN">
                <f:localized resid="ERR_FILENAME_PATTERN"/>
              </f:property>
            </f:properties>
          </f:validator>
          <f:validator
            class="net.sf.jguiraffe.examples.tutorial.createfile.UniqueFileNameValidator"/>
        </f:validators>
      </f:textfield>
      <f:label textres="newfile_lab_content">
        <f:percentconstr col="1" row="3"/>
      </f:label>
      <f:textarea name="fileContent" displayNameres="newfile_disp_content">
        <f:percentconstr col="1" row="4" spanx="3" targetCol="3"/>
        <f:font name="Monospaced" size="13"/>
        <f:validator phase="syntax" beanName="requiredValidator">
        </f:validator>
      </f:textarea>
    </f:panel>

    <!-- The button bar -->
    <f:panel>
      <f:borderconstr name="SOUTH"/>
      <f:buttonlayout/>
      <f:button name="btnOk" textres="newfile_btn_create"
        mnemonicres="newfile_btn_create_mnemo" default="true"/>
      <f:button name="btnCancel" textres="newfile_btn_cancel"
        mnemonicres="newfile_btn_cancel_mnemo" cancel="true"/>
    </f:panel>
  </w:dialog>
</j:jelly>
    

The listing is a bit verbose as XML documents tend to become in general. However, it should be understandable which parts are responsible for the creation of which UI elements.

After the typical namespace declaration which includes all of the JGUIraffe tag libraries there is a block that uses the tags of the dependency injection tag library to define some validator objects. These objects are later assigned to the text fields to ensure that they can contain only valid data.

The interesting part starts with the <w:dialog> tag that introduces a new dialog window. In the body of this tag the definition of the dialog's user interface is placed. For the dialog window as a whole a border layout is defined by the <f:borderlayout> tag (refer to the Layouts section for more details about the dialogs supported by JGUIraffe). The UI of the dialog consists of two panels that are arranged using the border layout: the main panel in the center, and the buttons panel in the south. Both are defined by <f:panel> tags in the body of the <w:dialog> tag. The content of the panels is again defined in the body of these tags. This nesting of tags is a fundamental principle used within builder scripts. It should not be hard to understand because it corresponds to the structure of the UI elements created. The layout constraints that tell the layout manager where to place the panels are specified by nested tags in the body of the <f:panel> tags (<f:borderconstr> in this case). This is the typical way to specify layout constraints: they are placed in the body of the tag they refer to, too.

The first panel is the more complex one. It uses a PercentLayout layout manager to layout the components it contains. The layout is defined by the <f:percentlayout> tag in the body of the panel. The attributes of the tag specify the columns and rows comprising the tabular layout. To actually place components in this layout, <f:percentconstr> tags are needed that define the position and other properties of components which belong to the panel. Again these tags can be found in the bodies of the tags defining the components they refer to.

The main panel consists of two labels and two text fields (a single line text field and a text area with multiple lines). Labels are pretty easy to define. For this example the labels are only assigned texts - they support icons, too, but this is not needed here. The texts of the labels are specified using the textres attribute. The suffix "res" stands for resource; this means that the actual text of the label is loaded from the application's resources using the value of the attribute as resource ID. (More information about resources can be found in the section Resources.) Here we see another common principle used within the JGUIraffe tag libraries: when texts are to be defined that are displayed to the end user, there are always the following possibilities:

  • The text can be specified directly in the builder script. In this case it is hard-coded and does not react on the user's locale. Attributes that allow setting texts directly do not have any specific suffix. For instance, the <f:label> tag has the attribute text that takes the text of the label.
  • The text can be defined by a resource ID that is looked up in the application's resources. This is done by an attribute with the suffix "res" as the textres attribute in the example script. As resource group the application's default resource group is used.
  • If the resource group should be different from the default resource group, it has to be specified using another attribute. These attributes end on the suffix "grp". For the resource group of a label's text the attribute is named resgrp.
Such a triple of attributes is typical for many tags. Other examples taken from the script above are the titleres attribute of the <w:dialog> tag and the displayNameres attribute of the <f:textarea> tag.

The text input components are more complex. The corresponding tags have more attributes than the simple <f:label> tag, e.g. a tooltip and a display name. For the text area a special font is set using a nested <f:font> tag. Fonts can be set this way for all kinds of graphical elements. It is also possible to share font objects between multiple elements. This is achieved by specifying the var attribute of the <f:font> tag. Then the font is stored under this name in the current context. It can be referenced by other tags for graphical elements using their fontRef attribute. Refer to the documentation of the FontTag class for more details.

But what makes the tags for the text components really complex are the validator definitions in their bodies. For the text area a required validator is specified which checks whether the field contains a value - it is not permitted to leave the field empty. A nested <f:validator> tag is used to assign the validator to the text area. The other text field contains even multiple validators. Therefore the <f:validators> tag occurs in the tag's body which in turn contains three nested <f:validator> tags. This means that on validation of the text field all its validators are invoked. Only if all of them return a valid result, the text field is considered valid.

The <f:validator> tag is derived from the UseBeanBaseTag base tag handler class. The example script contains different variants of this tag demonstrating the different ways to obtain beans:

  • If the beanName attribute is used, there must be a bean definition with this name in the current script or in the parent bean context. In this example validator beans are referenced that are defined at the beginning of the script using <di:bean> tags.
  • Another variant uses the class attribute rather than beanName. This causes an instance of the specified class being created using its standard constructor - which is convenient, but less powerful than the variant that uses the full power of the dependency injection framework.
Regardless of the way the validator bean was created, it is passed to the nesting input element tag.

This concludes our discussion of the first example builder script. You should now have an impression of how builder scripts look like and know the basic principles of defining UI elements. In the next section we discuss the tags for creating specific UI elements.

The tags of the component builder tag library

In this sub section we present the tags that comprise the component builder tag library. The single tags are introduced, and their implementing tag handler classes are noted. We give short usage examples and mention points that may not be obvious or may help to avoid pitfalls. Note that this is not a reference of the tags with all their attributes. Reference information can be found in the Javadocs of the tag handler classes (which are linked from the describing text).

Labels and icons

Labels belong to the most simple graphical components. They just display a text and/or an icon which cannot be changed at runtime. Nevertheless, there is a bunch of attributes available allowing to customize the display of the label. A description of all available attributes can be found in the Javadocs of the LabelTag class.

As was already described in the subsection A simple example, the text of a label can be defined either directly by specifying the text attribute or via a resource ID using the textres attribute. In addition, a label can have an icon. Icons are defined by nested <f:icon> tags which are implemented by the IconTag tag handler class. The following fragment shows an example of a more complicated label declaration:

<f:label text="Hello world!" foreColor="blue" mnemonic="w" alignment="center"
  componentref="TestComponent">
  <f:icon resource="icon.gif"/>
</f:label>
    

Here some more attributes of the <f:label> tag are used. A text is directly assigned, and an icon is specified by a nested <f:icon> tag. The alignment attribute determines the alignment of the label's text. With componentref another component (which must be defined in the same builder script) can be specified; this will associate the label with this component, so that pressing the mnemonic key of the label sets the focus on the associated component.

The <f:icon> tag supports various ways to obtain an icon. Here we use the resource attribute which looks up the icon in the class path. It is also possible to provide a URL from which the icon is to be loaded. The most generic way is to specify a Locator object via the locator attribute. The Locator is looked up in the current bean context.

Panels

Panels are UI elements whose most important functionality is to group other components together. Allthough they are visible components, usually it is not their appearance what makes them special, but the fact that they operate as a container for components defining their own layout. In Java applications complex layouts are often implemented by nesting multiple panels.

Complex nesting of panels should not be necessary for JGUIraffe applications because the provided layout managers like percent layout allow the arrangement of components in a flexible and powerful way. Nevertheless it is often convenient to group a UI into at least two panels. The example dialog box presented in the A simple example subsection used two panels: one for the main part of the dialog's UI and one for the buttons at the bottom. This is a typical pattern.

Panels are defined using the <f:panel> tag implemented by the PanelTag class. The content of the panel is defined by tags in the body of the <f:panel> tag. In the most typical use case a <f:panel> tag does not have any attributes, but defines a layout manager in its body and nests tags for other components. This looks as in the following example:

<f:panel>
  <f:borderlayout/>
  <f:textarea name="mainText">
    <f:borderconstr name="CENTER"/>
  </f:textarea>
</f:panel>
    

Here a panel with a border layout is defined. A text area is placed in the center part of its UI. (We cover tags for layouts soon.) The panel merly acts as background for the componets placed on it. The PanelTag class supports some attributes which modify the panel's appearance. Of course, the standard attributes for visible components are available, e.g. for changing the foreground or background colors. It is also possible to define a text as a caption for the panel or to instruct the panel to draw a border. A more complex example is shown below:

<f:panel backColor="black" border="true" textres="PANEL" textColor="YELLOW"
  resgrp="testformbuilderresources">
...
</f:panel>
    

Layouts

The JGUIraffe library ships with several custom layout manager implementations. For each of them the component builder tag library contains corresponding tags for creating layouts and defining layout constraints. The following table displays the associations between layout manager class, tag, and tag handler class.

Layout manager Tag Tag handler class
PercentLayout <f:percentlayout> PercentLayoutTag
BorderLayout <f:borderlayout> BorderLayoutTag
ButtonLayout <f:buttonlayout> ButtonLayoutTag

PercentLayout is the most powerful layout manager provided by JGUIraffe. The corresponding <f:percentlayout> tag allows creating such a layout with all possible properties. There are two variants for creating a percent layout: The first variant is to specify the column and row constraints as strings as in the following example. This corresponds to the constructor of the PercentLayout class which accepts to strings:

<f:percentlayout columns="4dlu end/preferred 3dlu start/preferred 4dlu"
  rows="4dlu preferred 1dlu preferred 1dlu preferred 1dlu preferred 4dlu"/>
    

The strings passed to the attributes must be valid constraints definitions. The other variant uses nested <f:colconstr> and <f:rowconstr> tags for defining the cell constraints. This looks as follows:

  <f:percentlayout canShrink="true">
    <f:colconstr constr="4dlu"/>
    <f:colconstr constr="0.5cm"/>
    <f:colconstr constr="start/preferred"/>
    <f:colconstr constr="3dlu"/>
    <f:colconstr constr="full/preferred"/>
    <f:colconstr constr="3dlu"/>
    <f:colconstr constr="preferred"/>
    <f:colconstr constr="3dlu"/>
    <f:colconstr constr="full/preferred"/>
    <f:colconstr constr="4dlu"/>

    <f:rowconstr constr="4dlu"/>
    <f:rowconstr constr="preferred"/>
    <f:rowconstr constr="1dlu"/>
    <f:rowconstr constr="start/minimum(2cm)"/>
    <f:rowconstr constr="3dlu"/>
    <f:rowconstr constr="preferred"/>
    <f:rowconstr constr="1dlu"/>
    <f:rowconstr constr="preferred"/>
    <f:rowconstr constr="3dlu"/>
    <f:rowconstr constr="preferred"/>
    <f:rowconstr constr="1dlu"/>
    <f:rowconstr constr="preferred"/>
    <f:rowconstr constr="4dlu"/>

    <f:colgroup idx1="4" idx2="8"/>
  </f:percentlayout>
    

Here each nested tag defines column or row constrains for a single cell. The corresponding tag handler classes are PercentColConstraintsTag and PercentRowConstraintsTag respective. As can be seen with the <f:colgroup> tag column groups can be defined. Analogously the <f:rowgroup> tag defines groups for rows. This also works if the column and row constraints are defined as attributes. The corresponding tag handler classes are PercentColGroupTag and PercentRowGroupTag. Also note the canShrink attribute added to the <f:percentlayout> tag. It tells the tag that is should set the canShrink property of the generated layout.

When working with PercentLayout each component to be added to the layout must be associated with a PercentData constraints object. In a builder script this is achieved by placing the <f:percentconstr> tag in the body of the tag defining the associated component. <f:percentconstr> is implemented by the PercentConstraintsTag class and supports attributes for setting all properties of a PercentData object, e.g. the position in the grid, the span, the target cell, or additional constraints. The following fragment shows an example of using this tag:

<f:label textres="viewset_lab_filtertypes">
  <f:percentconstr col="1" row="1" spanx="2"/>
</f:label>
    

Another layout manager supported by JGUIraffe is border layout. Layouts of this type can be created using the <f:borderlayout> tag implemented by the BorderLayoutTag class. This tag provides attributes to set all the properties supported by a BorderLayout object, namely the margins and gaps between the areas of the layout. The constraints used by BorderLayout are strings defining one of the five possible areas (NORTH, EAST, SOUTH, WEST, CENTER) in which a component is to be placed. They are defined by <f:borderconstr> tags placed in the bodies of the tags for the associated components. The following example demonstrates how this looks in practice:

<f:borderlayout leftMargin="0.5cm" rightMargin="0.5cm" topMargin="0.25cm"
  bottomMargin="0.25cm" northGap="2dlu" southGap="2dlu">
  <f:label text="Label in the north">
    <f:borderconstr name="NORTH"/>
  </f:label>
  <f:panel>
    <f:borderconstr name="CENTER"/>
    <!-- Content of panel in the center -->
    ...
  </f:panel>
</f:borderlayout>
    

A ButtonLayout is specialized on placing buttons in a dialog box. The corresponding tag is <f:buttonlayout>, implemented by the ButtonLayoutTag class. Again the properties of the layout are defined using attributes: it is possible to set the margins of the layout, the gap between the buttons, and the alignment of the whole button bar (left, right, or center). For a button layout no constraints are needed because the buttons are placed in the layout in the order they are added. The following listing shows an example:

<f:panel>
  <f:buttonlayout leftMargin="0.25cm" rightMargin="0.25cm" gap="0.5cm"
    align="right"/>
  <f:button name="btnOk" textres="viewset_btn_save"
    mnemonicres="viewset_btn_save_mnemo" default="true"/>
  <f:button name="btnCancel" textres="viewset_btn_cancel"
    mnemonicres="viewset_btn_cancel_mnemo" cancel="true"/>
</f:panel>
    

Static texts

We already discussed labels which are used to output texts and icons the user cannot manipulate. Labels are immutable; once created they cannot be changed any more. Sometimes an application wants to display text and/or icons that can change dynamically. For this purpose the component builder tag library provides the <statictext> tag which is implemented by the StaticTextTag class.

The definition of a static text is very similar to a regular label definition. The <statictext> tag supports the same attributes. However, under the hood it creates a fully-functional input component for the text - which means that there is an associated ComponentHandler for it. The component handler can be accessed from the ComponentBuilderData object using the name specified in the <statictext> tag's name attribute. It can be cast to the specialized handler interface StaticTextHandler which provides convenient methods for manipulating the text and the icon directly. Note that per default no field is created for the static text element in the form object created by the builder. The following fragment of a builder script shows the declaration of a static text element:

<f:statictext name="staticText" textres="STATIC_TEXT" alignment="right">
  <f:icon resource="icon.gif"/>
</f:statictext>
    

As can be seen, a static text can be assigned a text and an icon in the same way as a label. Given this declaration, the element can be manipulated (e.g. by a form controller class) in the following way:

StaticTextHandler handler = (StaticTextHandler)
  getComponentBuilderData().getComponentHandler("staticText");
handler.setText("new text");
    

The following rule of thumb can be used to determine whether a plain label or a static text element should be used: If the element is not changed during the life time of the application, use a label. Otherwise take a static text and manipulate it accordingly.

Although there exists a ComponentHandler for each static text element, the handlers are not added to the form object constructed for the UI per default. This is typically not desired because these elements are not used to gather user input and therefore do not need a corresponding property in the model object of the form. However, the default behavior can be changed by setting the noField attribute of the <statictext> tag to false. The tag should then also be given a name. Then it is possible to write data from the model object of the form into the UI elements.

There is one problem however: StaticTextHandler expects that it is passed data as StaticTextData objects; therefore the properties of the form's model bound to static text elements must be of this type. This is inconvenient for instance if the elements are used to display mutable text only. In order to simplify this use case, JGUIraffe provides a special transformer implementation which can convert data to StaticTextData objects: StaticTextDataTransromer. Among other things, this class is able to transform a plain text string into a StaticTextData object which can then be used to initialize the static text element. So if this transformer is applied, properties of the form's model object can be simple types. We will see in a moment how transformers are assigned to UI elements.

Transformers

Often an application has the requirement that a user enters data of specific data types. As an example consider an order entry application which allows the user to enter the quantity of the product to be ordered. Input fields typically do not know about numbers, but only operate on text. So the text data used internally by the input component has to be converted to a number to be compatible with the model of the form. Analoguously, if the input fields are populated from the model of the form, another data type conversion has to be performed from number to string. This is where transformers come into play. They can handle arbitrary conversions of form data. Refer to the section Transformers and Validators for more background information.

Every input component can be assigned up to two transformers:

  • The read transformer is invoked when the data entered by the user is read and stored in the form's model.
  • The write transformer is invoked when the input components are populated with data from the form's model.
Transformers are specified using <f:transformer> tags placed in the bodies of input component tags. The tag handler implementation class is TransformerTag. Have a look at the following example:
  <!-- Transformer for converting arbitrary data to string -->
  <di:bean name="stringTransformer"
    beanClass="net.sf.jguiraffe.transform.ToStringTransformer">
  </di:bean>

  <!-- A validator/transformer for date input fields.-->
  <di:bean name="dateTransformer"
    beanClass="net.sf.jguiraffe.transform.DateTransformer">
  </di:bean>

  ...

    <f:textfield name="dateField">
      <f:transformer type="read" beanName="dateTransformer"/>
      <f:transformer type="write" beanName="stringTransformer"/>
    </f:textfield>
    

Here a field is defined that allows the user to enter a date. This is implemented by assigning two transformers to the text field: the read transformer is able to parse a string into a date; the write transformer can transform arbitrary objects to strings. Both transformers are defined using <di:bean> tags and referenced by the <f:transformer> tags. Because TransformerTag is derived from UseBeanBaseTag it can also directly create transformer objects. However, using the <di:bean> tag for this purpose is more powerful. This also allows sharing the transformers for multiple components (which does not cause any problems; because the UI is accessed only by a single thread there can be no race conditions): just reference the transformer bean from multiple <f:transformer> tags. By the way, the transformers used in this example are standard transformers shipped with the JGUIraffe library. The standard transformers support many primitive data types so that it is easy to implement text fields for entering data in these types.

One note: When working with transformers it must be ensured that the data entered by the user can actually be transformed into the target data type; otherwise the transformer throws an exception. The easiest way to do this is to add a corresponding validator to the input component. The validator is invoked first, and only if it signals that the data is valid, the transformer is called.

Validators

The handling of validators in builder scripts is pretty similar to the handling of transformers. Actually, the tag handler class for validators, ValidatorTag, is derived from the same base class as the tag handler class for transformers. Like transformers, validators are defined by tags nested in the bodies of input element tags. This time the name of the tag is <f:validator>. It supports the same attributes for creating or referencing the validator object. In addition the phase attribute is available which controls when the validator is invoked. It accepts the following values:

  • syntax means that the validator is invoked directly on the raw data obtained from the associated input element. It has to check whether this data can be converted to the desired target data type (i.e. whether the read transformer can be called successfully). This is the standard value if the phase attribute is not provided.
  • logic means that the validator is invoked after the read transformer. Thus it has access to the converted value. It can perform checks on a logic level, e.g. whether the value fulfills certain constraints.
Let's extend the example from the Tranformers section to specifiy a validator, too:
  <!-- Transformer for converting arbitrary data to string -->
  <di:bean name="stringTransformer"
    beanClass="net.sf.jguiraffe.transform.ToStringTransformer">
  </di:bean>

  <!-- A validator/transformer for date input fields.-->
  <di:bean name="dateTransformer"
    beanClass="net.sf.jguiraffe.transform.DateTransformer">
  </di:bean>

  ...

    <f:textfield name="dateField">
      <f:transformer type="read" beanName="dateTransformer"/>
      <f:transformer type="write" beanName="stringTransformer"/>
      <f:validator phase="syntax" beanName="dateTransformer"/>
    </f:textfield>
    

The only change is that a <f:validator> tag was added. Note that it references the same bean as the tag for the read transformer. This is because many of the standard transformers provided by JGUIraffe are also validators. So they can play both roles.

Transformers and validators specified using the corresponding tags can be configured using additional properties. One use case is to override some properties of the transformer or validator object. As was already pointed out, transformers and validators can be shared between multiple input components. Now it is possible that many components operate with standard settings of the objects, but a single component has some special requirements - for instance, a different format for the values should be used. Instead of defining two transformer beans for the different requirements, a single one can be used. For the input component with special requirements properties can be defined which override the settings to be adapted. As an example consider a validator for numbers. We might have multiple input components that allow the user to enter arbitrary numeric values. Then there is a special component in which the number must be greater than a given value. This can be achieved with the following declarations:

  <!-- A validator/transformer for numeric input fields.-->
  <di:bean name="numberTransformer"
    beanClass="net.sf.jguiraffe.transform.IntegerTransformer">
  </di:bean>

  ...

    <f:textfield name="specialField">
      <f:validator phase="syntax" beanName="dateTransformer">
        <f:properties>
          <f:property property="minimum" value="25"/>
        </f:properties>
      </f:validator>
    </f:textfield>
    

Here a generic transformer/validator for numeric input is defined as a bean. For the special input field this object is referenced, but additional properties are provided which define a minimum value. Properties are implemented by the PropertiesTag tag handler class. The single properties are defined by nested <f:property> tags implemented by PropertyTag. Sometimes the values of properties are texts to be displayed to the user, e.g. special validation error messages. In this case, the texts may need to be localized so that they are in the language of the current user. This can be achieved by using the <f:localized> tag in the body of <f:property>. This tag is implemented by the LocalizedTag class. It can be specified a resource ID which is resolved to obtain the property's value. The following example shows how a validator can be configured to create a customized error message which is obtained from the application's resources:

<f:textfield name="specialField">
  <f:validator phase="syntax"
    class="net.sf.jguiraffe.transform.IntegerTransformer">
    <f:properties>
      <f:property property="ERR_INVALID_NUMBER">
        <f:localized resid="my_error_resource"/>
      </f:property>
    </f:properties>
  </f:validator>
</f:textfield>
    

Using these tags it is easy to adapt the error messages produced by validators. All validator classes document in their Javadocs the keys of the error messages they can produce. By setting a property with a specific key the corresponding error message can be customized. The <f:localized> tag can be used to select the message text in the correct language.

One difference between transformers and validators is that an input element can have an arbitrary number of validators while there is only a single read or write transformer. This can make sense if there are multiple validation rules to be checked. To define multiple validators the <f:validators> tag (implemented by the ValidatorsTag class) is used. In the body of this tag multiple <f:validator> tags can be placed which are collected and combined to a single ChainValidator object. This looks as follows:

  <f:textfield name="fileName">
    <f:validators phase="syntax">
      <f:validator beanName="requiredValidator"/>
      <f:validator beanName="fileNameValidator"/>
      <f:validator
        class="net.sf.jguiraffe.examples.tutorial.createfile.UniqueFileNameValidator"/>
    </f:validators>
  </f:textfield>
    

Text components

Input fields for texts belong to the most basic components used within forms. Of course, they are used to enter arbitrary text input from the user, but by assigning appropriate transformers and validators (refer to the corresponding sections), other data types - like number or date - are supported, too.

The component builder tag library provides three tags for creating text input components for different purposes:

  • <f:textfield> (implemented by the TextFieldTag class) produces plain single-line text input fields. They can be configured by specifying the number of columns to be displayed - which gives a hint about the preferred width of the text field - and the maximum length of the text to be entered.
  • <f:password> (implemented by the PasswordFieldTag class) generates an input field for entering a password. A password field is pretty similar to a regular text field and allows the same configuration options. The only difference is that the characters typed by the user are not directly displayed, but only an echo character is printed.
  • <f:textarea> (implemented by the TextAreaTag class) is responsible for the creation of multi-line text input fields. These fields can be used to enter larger amounts of text. They also have built-in support for scrollbars if the text becomes larger than the visible area on the screen. A text area can be configured with the number of columns and rows to be displayed which influence the size of the component. In addition the maximum length of the text to be entered can be specified and a flag whether automatic line wrapping is to be performed.
The following listing gives some examples of declarations for text components:
<f:textfield name="name"/>
<f:textfield name="firstName" columns="20"/>
<f:textfield name="street" maxlength="30"/>
<f:textfield name="city" columns="20" maxlength="35"/>
<f:password name="pwd" columns="8" maxlength="10"/>
<f:textarea name="remarks" columns="40" rows="5" maxlength="1000" wrap="true"/>
    

One pitfall when working with text components is the sizing of the fields. If a percent layout is used, and the cell constraints are set to preferred size, the underlying GUI toolkit tries to determine the size of the text field. At least for Swing the results are not really satisfying. If no specific configuration options are set, Swing assumes very small sizes; you often end up with just a narrow white bar on the screen. To avoid this, you can specify the number of columns to be displayed. This makes the text field wider, but does not allow a precise sizing. With percent layout you can also specify a minimum cell size. This is well suited for working with text fields. If a text field is placed in a cell with a minimum width of - say - 4 cm, the sizing problem is solved. An alternative for determinining the size of the text component is described in the next section which is about scrolling support.

For text components JGUIraffe provides a specialized component handler interface: TextHandler. Through this interface extended functionality available for text components can be accessed. For instance, it is possible to query or manipulate the current selection, or to interact with the system clipboard. The ComponentHandler returned for a text component can be cast to TextHandler.

Scrolling support

Some input components allow handling of large amounts of data. For instance, a user might want to type a whole play of Shakespeare into a text area. Of course, this is more text than fits onto the screen. Therefore such components only display a portion of the data they contain and provide scroll bars allowing the user to navigate to other parts of their data. This is a well-known concept of windowing systems every user should be familiar with.

If you have ever worked with Swing, you probably know that Swing controls do not support scrolling on their own. Rather, components like text areas or tables have to be added to a javax.swing.JScrollPane. The scroll pane implements the whole scrolling logic; it queries the total size of the wrapped component, determines the preferred size on the screen, and displays scroll bars as necessary. The JGUIraffe library uses a different approach: It does not provide a scroll pane component. Instead, components that may become larger than the screen size are automatically added to a scroll pane. This is fully transparent to the developer.

There is, however, one aspect related to scrolling the developer should be aware of: the preferred screen size of components that allow scrolling. In most cases, because of the scrolling capabilities, the screen size of such components does not really matter to make them usable: even if a text area is very small, by using the provided scroll bars the user can read the full text it contains. Because of that it is hard to determine a preferred screen size automatically, and the developer might want to give some hints. Text area components for instance can be initialized with the number of columns and rows they should display (as shown in the section Text components). Based on this information the preferred screen size can be calculated. For other components, e.g. tree views or tables, there are no such obvious ways to indicate a preferred size on the screen.

The component builder tag library provides a standard means to set the preferred screen size of scrolling-enabled components. All tags which define components of this category support the attributes scrollWidth and scrollHeight. Here the developer can provide a string value compatible with the NumberWithUnit class, e.g. 10 cm or 100 px. Both attributes are optional, if one is missing, the corresponding size is set to a default value based on the component affected. The following example shows how the preferred height is set for a list box component (lists are described in the section Lists and combo boxes):

<f:list name="fileTypes" multi="true" scrollHeight="3cm">
  ...
</f:list>
    

Here it is specified that the preferred screen height of the list is 3 centimeters. The layout manager will take this information into account when determining the sizes of the components in the hosting window. (Of course, the list component can later grow or shrink depending on the layout settings and the total space available.) The preferred scroll width has not been specified, so here a default value is used - in this case the value is calculated based on the widths of the list elements to be displayed.

The scrollWidth and scrollHeight attributes are supported by tags for the following UI elements:

Checkboxes

Checkboxes are typically used to let the user enter data of type boolean. They are also part of the set of standard input components. In JGUIraffe checkboxes are created using the <f:checkbox> tag which is implemented by the CheckboxTag class.

Using checkboxes is pretty easy. Actually the attributes supported by the <f:checkbox> tag are very similar to the ones available for labels. You can

  • specify a text for the checkbox - either directly or via a resource ID
  • define an icon using a nested <f:icon> tag
  • set an alignment
  • define a mnemonic so that the checkbox can be triggered using keyboard short cuts.
Of course, the standard attributes for input elements and visual components are also available. The following fragment shows some examples of checkbox declarations:
<f:checkbox name="fine" text="Everything fine?" foreColor="blue" mnemonic="f"/>
<f:checkbox name="checkIcon" backColor="black" alignment="center">
  <f:icon resource="icon.gif"/>
</f:checkbox>
<f:checkbox name="cbx1" textres="CHECK1" resgrp="testformbuilderresources"/>
<f:checkbox name="cbx2" textres="CHECK2" mnemonicres="CHECK2"/>
    

Component groups

Sometimes a number of components logically belongs together. As an example consider a dialog box consisting of several areas where each area is specific to a use case. Depending on the state of the application or the dialog the components in some of the areas may not be available. In this scenario it would be convenient to have a means for grouping components together: groups could be created for the different areas, and then it would be possible to enable or disable all components in a group in a single step.

The component builder tag library supports groups of components. Here groups are non-visual components. They are represented by the ComponentGroup class. A ComponentGroup is a logic construct which only stores the names of the elements that are part of the group. The class provides some methods to obtain the actual components or their component handlers. A few operations for manipulating the elements are available, too. To access the components in a group, usually a ComponentBuilderData object must be available; this is required for resolving the component names.

In a builder script the <f:group> tag is used to create a component group. As a mandatory attribute the (unique) group name must be provided. For assigning components to groups there are two options:

  • Each input element tag supports the group attribute. Here a comma-separated list with names of component groups can be specified. The component is then added to all of these groups (a single component can belong to multiple groups).
  • Tags for input elements can also be placed in the body of a <f:group> tag. They are then automatically added to the corresponding component group unless they have a different groups attribute. This is a very compact and readable way to define a component group because it is pretty obvious which elements are part of the group. However, depending on layout requirements it may not always be possible to place all group members into the tag body. In this case the other option is needed.
Here are some examples for the definition of component groups:
<f:group name="group1"/>
<f:group name="group2"/>

<f:textfield name="text1" groups="group1"/>
<f:textfield name="text2" groups="group1, group2"/>

<f:group name="group3">
  <f:textfield name="text3"/>
  <f:textfield name="text4"/>
  <f:textfield name="text5" groups="group2"/>
</f:group>
    

In the first block two <f:group> tags with empty bodies are used. This creates two empty component groups. The groups are populated later on with components that have the groups attribute set accordingly. Creating the groups first is mandatory because the names passed to the groups attribute must reference existing component groups. For the component group with the name "group3" the alternative option is used: here all of its elements are declared in the body of the tag. Note that one element in the body specifies a different group in its groups attribute, so it won't be added to this group.

After executing this builder script the component groups can be accessed from the bean context created during the builder operation. The ComponentGroup class defines the static fromBeanContext() method for this purpose. In the subsection Fundamental classes and interfaces there was an example of how the enabled status of a component could be changed based on a clicked checkbox. We can now extend this example to manipulate a whole group of components that all depent on this checkbox. So there is again a form controller which is registered as change listener at the checkbox. Whenever the checkbox is changed the event listener method determines the current state of the checkbox and enables or disables all elements of the group accordingly. The following listing shows this use case:

/** Constant for the name of the check box component. */
private static final String COMP_CHECKBOX = "testCheckBox";

/** Constant for the name of the dependent component group. */
private static final String GROUP_NAME = "dependentComponents";

/**
 * Notifies this object about a change in the data of an element.
 * @param e the change event
 */
public void elementChanged(FormChangeEvent e)
{
    // obtain the handler for the check box (the type-safety warning can be
    // suppressed because we know that it is a check box handler
    @SuppressWarnings("unchecked")
    ComponentHandler<Boolean> checkHandler = (ComponentHandler<Boolean>)
        getComponentBuilderData().getComponentHandler(COMP_CHECKBOX);
    enableGroup(GROUP_NAME, handler.getData());
}

/**
 * Helper method for enabling a component group.
 *
 * @param groupName the name of the group
 * @param enabled the enabled flag
 */
private void enableGroup(String groupName, boolean enabled)
{
    ComponentGroup group = ComponentGroup.fromBeanContext(
            getComponentBuilderData().getBeanContext(), groupName);
    try
    {
        group.enableGroup(getComponentBuilderData(), enabled);
    }
    catch (FormBuilderException fex)
    {
        // should normally not happen
        log.warn("Error when enabling group " + groupName, fex);
    }
}
    

In this example the helper method enableGroup() enables or disables all components that are part of the specified component group. When dealing with component groups, in most cases a reference to the ComponentBuilderData object is needed. First the ComponentGroup object representing the desired group is obtained through the static fromBeanContext() method. The bean context is queried from the ComponentBuilderData object. ComponentGroup provides the enableGroup() method which sets the enabled state of all components that belong to the group. Again a reference to the ComponentBuilderData object is required for this purpose to resolve the components. This method can throw an exception if a component cannot be resolved.

Radio buttons

After the excursus about Component groups in the last subsection we can now describe radio buttons. The understanding of component groups is a prerequisite for the understanding of radio buttons.

In JGUIraffe applications radio buttons are created using the <f:radio> tag which is implemented by the RadioButtonTag class. From its properties and its underlying data a radio button is similar to a checkbox. Actually, the <f:radio> tag shares many of the attributes supported by the <f:checkbox> tag: again a text (either directly or as resource ID), an icon, the alignment, and a mnemonic can be specified. Like a checkbox a radio button can either be checked or not, therefore it is associated with a ComponentHandler of type boolean.

What makes radio buttons special is the fact that they make only sense in groups: here the special property that only a single radio button can be selected comes into play. This property makes radio buttons surprisingly complex and also influences the data applications wish to store for radio buttons. Typically an application does not want to model a group of radio buttons as a set of boolean flags telling for each radio button whether it is selected or not. Rather, a single information is to be stored for the whole group indicating which of the radio buttons is selected. This could be a numeric index of the selected radio button in the group or maybe a value of an enumeration class. So from a logic point of view radio buttons are not treated as single, independent input components, but they are combined to a radio group which is handled as an input component on its own.

The component builder tag library supports this use case by providing another tag for creating radio groups: the <f:radioGroup> tag implemented by the RadioGroupTag class. <f:radioGroup> can be used in the same way as the <f:group> tag: The tag has a mandatory name attribute that defines the name of the radio group, and the tags for the radio buttons that are part of the group can be placed in the body. Alternatively - if layout requirements do not permit such a nesting - radio buttons can be added to a radio group by specifying the group name in the groups attribute of the <f:radio> tag. The following fragment shows an example of how to define a radio group (layout-related tags have been omitted):

<f:radioGroup name="sortColumn">
  <f:radio name="sortColumnName" textres="viewset_rad_sortcolname"/>
  <f:radio name="sortColumnDate" textres="viewset_rad_sortcoldate"/>
  <f:radio name="sortColumnSize" textres="viewset_rad_sortcolsize"/>
</f:radioGroup>
    

The basic idea is now that only for the radio group as a whole a field is added to the current Form object, not for the single radio buttons. Therefore a ComponentHandler managing the data of the radio group is created. Per default, this is an instance of the DefaultRadioButtonHandler class. DefaultRadioButtonHandler has the data type java.lang.Integer; it determines the index of the selected radio button in the group and returns it as its data. This may be appropriate for simple use cases, but has the danger that the meaning of the index changes if the radio buttons are rearranged in the group. So you might want to provide a different ComponentHandler implementation.

This can be achieved by nesting a <f:componentHandler> tag (implemented by the ComponentHandlerTag class) in the body of the <f:radioGroup> tag. Here another component handler implementation can be specified. Writing a custom component handler for a radio group is easy because with AbstractRadioButtonHandler there is already a base class providing the major part of the required functionality. Concrete subclasses only have to define the mapping between the index of the selected radio button in the group and the data value to store for this group.

As a simple example consider a radio group with two radio buttons for the options "Yes" and "No". Because the user has only two options this can be modeled using a boolean value. (This radio group is equivalent to a single checkbox, but let's assume that due to layouting considerations radio buttons should be used.) So for the radio group a ComponentHandler implementation is needed that maps the data of the group to a java.lang.Boolean value. First we present the XML definition of the radio group:

<f:radioGroup name="yesOrNo">
  <f:componentHandler
    class="net.sf.jguiraffe.examples.tutorial.viewset.BooleanRadioButtonHandler"/>
  <f:radio name="radioYes" text="Yes"/>
  <f:radio name="radioNo" text="No"/>
</f:radioGroup>
    

The group is defined by two <f:radio> tags in the body of a <f:radioGroup> tag. New is the <f:componentHandler> tag which installs a custom component handler for the radio group. The following listing shows the implementation of the BooleanRadioButtonHandler class:

package net.sf.jguiraffe.examples.tutorial.viewset;

import net.sf.jguiraffe.gui.builder.components.model.AbstractRadioButtonHandler;

public class BooleanRadioButtonHandler extends AbstractRadioButtonHandler<Boolean>
{
    /** Constant for the index of the YES button.*/
    private static final int IDX_YES = 0;

    /** Constant for the index of the NO button.*/
    private static final int IDX_NO = 1;

    /**
     * Creates a new instance of {@code BooleanRadioButtonHandler}
     */
    public BooleanRadioButtonHandler()
    {
        super(Boolean.TYPE);
    }

    /**
     * Returns the index of the button with the specified value.
     *
     * @param value the data value of the radio group
     * @return the index of the selected radio button
     */
    @Override
    protected int getButtonIndex(Boolean value)
    {
        return value.booleanValue() ? IDX_YES : IDX_NO;
    }

    /**
     * Returns the value of the radio button with the specified index.
     *
     * @param idx the index of the selected radio button
     * @return the corresponding value for the whole radio group
     */
    @Override
    protected Boolean getDataForButton(int idx)
    {
        return idx == IDX_YES;
    }
}
    

The two methods getButtonIndex() and getDataForButton() perform the mapping between the index of the selected radio button and the data to be stored for the group. They are called by the base class when this information is needed. Here the index of the "Yes" button is mapped to the boolean value true and the index of the "No" button is mapped to false (and vice versa). More complex mappings can be done analogously. For instance, the index of the selected button could be mapped to an enumeration value.

As was stated before, single radio buttons are not added as fields to the current form per default. If this is desired, the noField attribute must be set to false explicitly.

One final note: So far validators were only mentioned in the context of text input fields. However, there is no reason why they cannot be applied to other input elements like radio groups as well. One use case would be to add a required validator to a radio group. This would ensure that the user has to select a button.

Lists and combo boxes

List boxes and combo boxes allow the user to select items from a given number of options. They are somewhat similar to a group of radio buttons, but typically they are used if there are more options available than would be suitable for a group of radio buttons. Apart from their visual representation list boxes and combo boxes have many things in common. In the JGUIraffe library they both use the same underlying data model: an object implementing the ListModel interface.

ListModel plays a similar role as the javax.swing.ListModel interface for Swing applications. It is not a complicated interface. It provides methods for querying the number of elements contained in the model and for querying the elements themselves - as value objects and as display objects. When a list or combo box is rendered the display objects returned by the model are used. When the data of such an input element is queried the data object is returned. Thus the items rendered on the screen can differ from the data that is actually stored for the input element. Refer to the Javadocs of the ListModel interface for a more detailed explanation.

The component builder tag library defines the tags <f:list> and <f:combo> for the definition of list boxes and combo boxes. They are implemented by the classes ListBoxTag and ComboBoxTag. Both tags require that the list model with the items to be displayed is set. The following options exist:

  • The modelRef attribute can be set to the name of a bean implementing the ListModel interface. This bean is looked up in the current BeanContext; so it may have been created for instance by a <di:bean> tag in the same builder script or passed to the builder in form of a builder property.
  • Alternatively a specialized tag in the body of the <f:list> or <f:combo> tag can define the list model. We will see which tags exist for this purpose in a moment.

Let's start with an example of the first option where the model is obtained from the current bean context. The example is about a file browser application. This application has a combo box that lists the root file systems available on the local machine (e.g. the existing drive letters on a windows system). The user can select a root file system whose content will then be displayed by the application. The fragment of the builder script that defines the combo box and its model looks as follows:

<di:bean name="fileSystemModel"
  beanClass="net.sf.jguiraffe.examples.tutorial.mainwnd.FileSystemListModel">
</di:bean>

<f:combo name="comboFS" modelRef="fileSystemModel" editable="false"/>
    

Here first a bean for the model is defined. Later on the model bean is referenced by the <f:combo> tag. Because the user can only select an existing item and cannot enter arbitrary text into the combo box's text field the editable attribute is set to false. If a list was to be used rather than a combo box, the declaration would be very similar: the <f:combo> tag would be replaced by a <f:list> tag. The editable attribute would be dropped because lists are per se not editable:

<f:list name="listFS" modelRef="fileSystemModel"/>
    

The implementation of the FileSystemListModel class representing the model of the combo box is shown below:

package net.sf.jguiraffe.examples.tutorial.mainwnd;

import java.io.File;

import net.sf.jguiraffe.gui.builder.components.model.ListModel;

public class FileSystemListModel implements ListModel
{
    /** Stores the available root objects. */
    private final File[] roots;

    public FileSystemListModel()
    {
        roots = File.listRoots();
    }

    /**
     * Returns the display object at the given index. This implementation
     * returns the name of the {@code java.io.File} representing the file system
     * root at the given index.
     *
     * @param index the index
     * @return the display object for this model element
     */
    @Override
    public Object getDisplayObject(int index)
    {
        return roots[index].getPath();
    }

    /**
     * Returns the data type of the model elements. For this model
     * implementation {@code java.io.File} is the data type.
     *
     * @return the data type of this model
     */
    @Override
    public Class<?> getType()
    {
        return File.class;
    }

    /**
     * Returns the value object at the given index. This implementation returns
     * the {@code java.io.File} object representing the file system root at the
     * given index.
     *
     * @param index the index
     * @return the value object at this index
     */
    @Override
    public Object getValueObject(int index)
    {
        return roots[index];
    }

    /**
     * Returns the size of this model.
     *
     * @return the size of this model
     */
    @Override
    public int size()
    {
        return roots.length;
    }
}
    

This implementation is not too complicated. The actual data the model operates on is already obtained in the constructor by calling File.listRoots();. The other methods mainly access the array returned by this operation. getDisplayObject() returns a string, namely the path of the File object representing a root file system. getValueObject() returns the File object itself. The ComponentHandler implementation of the combo box calls getValueObject() for the selected item and stores the result in the form's model. Therefore the data type of this model is java.io.File.

The other option to define the list model is to use special tags that are able to create a list model and to assign it to an enclosing <f:list> or <f:combo> tag. Currently the component builder tag library supports the <f:textListModel> tag which is implemented by the TextListModelTag class. As the name implies, this tag creates a simple text-based model. The model's content is defined by nested <f:listModelItem> tags. Here the implementing tag handler class is ListModelItemTag. In the body of a <f:textListModel> tag an arbitrary number of <f:listModelItem> tags can be placed. Each tag defines one item of the list model. Both the display text and the value object of the item can be defined. The Javadocs of the ListModelItemTag class describe all options available. The following fragment shows the definition of such a list model:

<f:list name="fileTypes" multi="true">
  <f:textListModel type="java.lang.String">
    <f:listModelItem text="*.exe" value="exe"/>
    <f:listModelItem text="*.txt" value="txt"/>
    <f:listModelItem text="*.java" value="java"/>
    <f:listModelItem text="*.scala" value="scala"/>
    <f:listModelItem text="*.xml" value="xml"/>
    <f:listModelItem text="*.jelly" value="jelly"/>
    <f:listModelItem text="*.html" value="html"/>
  </f:textListModel>
</f:list>
    

In this example a list is defined that allows the user to select multiple file types. The <f:listModelItem> tags define the items to be displayed in the list using the text attribute. In this case the values are strings, too. They could have been other objects as well - their data type must correspond to the type attribute of the <f:textListModel> tag. This approach - the definition of a list model directly in the builder script - can be used if the number of list items is fix and known beforehand. Otherwise, it is preferrable to create a specialized list model class as demonstrated in the previous examples.

The last example also demonstrates the multi attribute of the <f:list> tag. A value of true means that the user can select multiple list elements; otherwise the list allows only a single selection. The multi attribute influences the data stored in the form model for the list: if the list supports only single selection, the form model stores a single object of the data type of the list's model. In multi-selection mode an array of this data type is stored. So for the example above with the file types, the form model would have stored an array of strings for the list.

Progressbars

A progress bar is typically used to give feedback to the user about a longer running operation. Usually they are not used in forms for entering data. Nevertheless the component builder tag library supports these components with the <f:progressbar> tag which is implemented by the ProgressBarTag class.

When a progress bar is defined the following properties can be set:

  • The minimum value of the progress bar (0 per default).
  • The maximum value of the progress bar (100 per default).
  • The current value. This value must be between the minimum and the maximum. It determines the progress already made.
  • A text to be displayed for the progress bar. This text is displayed on top of the graphical progress bar. It can either be defined directly or via a resource ID.
The following fragment from a builder script shows an example:
<f:progressbar name="progress" text="progress" min="1" max="200"
  allowText="true"/>
    

A progress bar makes only sense if it can be updated at runtime during a long-running operation. To do this in a convenient way there is a specific ComponentHandler implementation for progress bars: ProgressBarHandler. This handler implementation allows setting the progress bar's current value and text directly. From a FormController implementation a progress bar could be manipulated as follows:

ProgressBarHandler handler = (ProgressBarHandler)
  getComponentBuilderData().getComponentHandler("progress");
handler.setValue(counter++);
    

Here we assume that this code snippet is executed repeatedly during a long-running operation. Each time it is executed another step of the operation is completed, so a counter is incremented. The value of the progress bar is set to this counter value. When the operation is finished the maximum of the progress bar should be reached. Note that for a progress bar per default no field is added to the current form. If this is desired, the noField attribute of the <f:progressbar> tag must be set to false explicitly. In this case the progress bar's value is stored in the form's model as an integer number.

Sliders

A slider allows the user to enter a numeric value in a given range. The user just drags the marker of the slider to the position that corresponds to the desired value. The value is stored in the model of the form as an integer value. There are some similarities between sliders and progress bars; both are initialized with a range of allowed values and display a current value. In contrast to a progress bar a slider allows the user to enter a value; hence it is a full-featured input component.

Sliders are defined using the <f:slider> tag which is implemented by the SliderTag class. The tag supports attributes for setting the following properties:

  • The minimum value of the slider. This is a required attribute.
  • The maximum value of the slider which is also required.
  • The spacing of ticks. A slider component can draw a line chart that displays the positions of the values. This makes it easier for the user to find the drag position that corresponds to a certain value. The tick spacing defines the distance between the lines in the line chart. The developer can define a major tick spacing and a minor tick spacing. For instance, the major tick spacing could be set to 10, and the minor tick spacing could be set to 5. This means that every 10 value units a thick line is drawn and every 5 units a thin line.
  • A flag whether ticks should be drawn. If set to true, the tick spacing should be defined, too.
  • A flag whether labels corresponding to the values should be drawn. If this flag is true, the slider components displays standard labels at the positions that correspond to major tick values.
The following snippet shows how a slider component can be defined:
<f:slider name="duration" min="0" max="30" minorTicks="5" majorTicks="10"
  showTicks="true" showLabels="true" tooltipres="bgtask_duration_tip">
    

The following figure shows a dialog with a slider that corresponds to this definition:

Slider example

Tabbed panes

Sometimes a single dialog should be used to let the user enter data related to several subtopics. An example could be the settings dialog of an application: There may be different areas with specific settings, e.g. general settings, security settings, directory settings, database settings, and so on. Rather than having a single dialog for each settings category, it may be more comfortable for the user to have a central settings dialog which allows navigating to the settings categories available. One way to achieve this is using a tabbed pane: The central settings dialog can define tabs for each category of settings. By clicking one of these tabs the corresponding page with settings is displayed.

The component builder tag library provides the <f:tabbedpane> tag to define a dialog with multiple tabs. <f:tabbedpane> is implemented by the TabbedPaneTag class. This tag defines a frame in which single tabs can be embedded. Tabs are defined using nested <f:tab> tags. Here the implementation class is TabTag. The following fragment of a builder script shows an example:

<f:tabbedpane name="tabs" placement="bottom">
  <f:tab titleres="viewset_tab_colors">
    <f:panel>
      ...
    </f:panel>
  </f:tab>

  <f:tab titleres="viewset_tab_sort">
    <f:panel>
      ...
    </f:panel>
   </f:tab>

  <f:tab titleres="viewset_tab_filters">
    <f:panel>
      ...
    </f:panel>
  </f:tab>
</f:tabbedpane>
    

The whole tabbed pane is defined by the enclosing <f:tabbedpane> tag. As an attribute the placement of the tabs can be specified - per default they are placed at the top of the pane. The tabbed pane can also be assigned a name, but this is optional. (Technically a tabbed pane is a regular input component and thus has a name. The data of this input component is the integer index of the selected tab. However, this information is normally not stored in the model of a form. Hence the noField attribute is set to true initially. It can be set to false explicitly to alter this behavior.)

In the body of the <f:tabbedpane> tag an arbitrary number of <f:tab> tags can occur each defining a page of the tabbed pane. <f:tab> supports attributes similar to a label: It is possible to set the text of the tab (either directly or as a resource ID), to define an icon and a mnemonic.

The content of the tab is defined in the body of the <f:tab> tag. Here a single component tag can be placed. Because a tab represents a whole page of a dialog and is usually more complex typically a <f:panel> tag is used here allowing the definition of an arbitrary complex UI.

At runtime only a single tab is active, and the content of this active tab is displayed. When the user navigates to another tab, the content of the old active tag is made invisible, and the content of the new one appears.

Buttons

Buttons play an important role in graphical user interfaces. Their main use case is within dialog boxes where they allow the user to close the window - either the data entered by the user is to be saved or editing is canceled. Generally speaking, a button represents an action which is executed when the user presses the button. Thus buttons also appear in tool bars where they provide fast access to central functionality of an application.

In this sub section we focus on the definition of command buttons that can appear in dialog or frame windows. Tool bars will be covered when the action builder tag library is discussed. This tag library is also responsible for the definition of listeners to be invoked when a button is clicked. The component builder tag library only provides tags for defining buttons, but does not deal with the events generated by button clicks.

Buttons are defined using the <f:button> tag implemented by the ButtonTag class. When used within dialogs they are frequently arranged by a button layout (see Layouts) as demonstrated by the following fragment:

<!-- The button bar -->
<f:panel>
  <f:borderconstr name="SOUTH"/>
  <f:buttonlayout/>
  <f:button name="btnOk" textres="viewset_btn_save"
    mnemonicres="viewset_btn_save_mnemo" default="true"/>
  <f:button name="btnCancel" textres="viewset_btn_cancel"
    mnemonicres="viewset_btn_cancel_mnemo" cancel="true"/>
</f:panel>
    

Here a panel is defined acting as the button bar of a dialog window. The panel is placed in the south of the dialog window. It uses a button layout with default settings and contains two buttons: an OK and a Cancel button.

The definition of a button is very similar to the definition of a Label. With the typical attributes the button's text, its mnemonic, and its alignment can be specified. With a nested <f:icon> tag the button can be assigned an icon. The default and cancel attributes are specific to buttons: Using these boolean attributes, one button of the window can be marked as the default or cancel button respective. This button can then be triggered when the user performs a UI-specific action (typically pressing Enter in case of the default button or Escape for the cancel button) - even if the button does not currently have the keyboard focus. Buttons can also be assigned a command. This is optional and may be of interest for event listeners registered at the button because the events generated by the button contain this command. But this will be discussed in more detail later.

Technically, buttons are input components, but they usually do not store data in the associated form. The ButtonTag class sets the noField attribute to true per default. Actually a button maintains a boolean value indicating whether the button is pressed or not. This is used by a special button type: a toggle button. Unlike a command button a toggle button typically does not trigger an action. Rather, if pressed by the user, it stays pressed until it is clicked again. This is semantically equivalent to a checkbox. For the definition of toggle buttons the component builder tag library provides the <f:toggleButton> tag implemented by the ToggleButtonTag class. <f:toggleButton> has the same attributes as <f:button> (except for the default attribute which is specific to command buttons). For the produced toggle button input components fields of type boolean are added to the associated form. The following snippet shows the definition of a toggle button:

<f:toggleButton name="toggle" text="Toggle" command="tog"/>
    

As was already pointed out, the reaction of button clicks is subject of a later section. Here we just want to mention that Form controllers provide automatic support for buttons that save or cancel a form: The FormController class has the properties btnOkName and btnCancelName. Here the names of the OK and the Cancel button can be provided. The controller then registers itself as event listener at these buttons and handles their events appropriately. This will be discussed in more detail in the section about windows.

Splitters

Sometimes it is necessary to divide the space available in a window on multiple components. Examples include a mail application showing a list of received mails in the top and the text of the currently selected mail in the bottom, or a directory browser application that displays a directory tree in the left and the content of the current directory in the right. Because the application does not how much space the user prefers for the single components it typically provides a bar which can be dragged to adopt the sizing of the components. This is what splitters are for.

In JGUIraffe applications a splitter can be added to a container component, e.g. a window (acting as top-level container) or a panel. It splits the space available either horizontally or vertically and provides it to exactly two subcomponents. The subcomponents can again be containers like panels with an arbitrary number of nested elements. It is even possible to nest splitter components to an arbitrary depth. While up to three nested splitters probably make sense (for instance a tree structure with mail folders on the left, a list with the mails received in the top, and the content of the selected mail in the bottom), the user should not be confused with too many nested splitters. The following code fragment shows a declaration of a user interface that works with splitters:

<f:splitter pos="50" size="10" resizeWeight="0.5">
  <f:panel>
    <!-- Content of the panel in the left -->
    ...
  </f:panel>
  <f:splitter pos="25" orientation="horizontal" resizeWeight="1">
    <f:panel>
      <!-- Content of the panel in the top -->
      ...
    </f:panel>
    <f:panel>
      <!-- Content of the panel in the bottom -->
      ...
    </f:panel>
  </f:splitter>
</f:splitter>
    

This examples skips the actual content of the panels comprising the UI, but it demonstrates all configuration options for splitter components. As shown in the code fragment above, splitters are defined using the <f:splitter> tag which is implemented by the SplitterTag class. Because splitters are no input components - no data is stored for them in the model of the current form - they do not require a name attribute. In addition to the standard attributes supported by all graphical components the following properties of a splitter can be configured:

  • The orientation of the splitter. This attribute determines whether the splitter splits the available space into an upper and a lower component - if the value is VERTICAL (which is the default) - or into a left and a right component - if the orientation is HORIZONTAL. (Note: The orientation is derived from the arrangement of the components managed by the splitter. For instance, in horizontal mode, the components are aligned horizontally while the splitter itself is a vertical bar. This may be a bit confusing.)
  • The size of the splitter can be specified. The splitter is displayed as a line between the components it separates. With this attribute the thickness of this line can be defined.
  • For the splitter an initial position (in pixels) can be specified. When the UI is initialized, the splitter is moved to this position. Using this attribute is a bit fragile due to different font sizes and screen resolutions. It is probably better to define the UI in a way that the components separated by the splitter can have their preferred sizes - if not specified otherwise, the sizes of the components determine the initial position of the splitter.
  • The behavior of the splitter when the size of the owning window changes can be specified as a so-called resize weight. This is a floating point number between 0 and 1 that controls how additional space is to be distributed between the two components managed by the splitter. A value of 0 means that all space is assigned to the first component (the left or the top component, depending on the splitter's orientation). A value of 1 causes all space to be assigned to the second component. A value of 0.5 will distribute the available space homogeneously between both components. Values less than 0.5 prefer the first component over the second one and vice versa.
As can be seen, the components to be managed by the splitter are just placed in the body of the tag. They are evaluated in the order of their appearance. There must be exactly two components, otherwise the processing of the <f:splitter> tag will throw an exception.

Splitters use the minimum size of the components they divide to determine constraints for their sizing. If a component already has its minimum size, the splitter cannot be dragged any further. This is a caveat, especially when working with percent layout managers. If not specified otherwise, the preferred size of a panel using percent layout is also its minimum size. So if two such panels are to be split, after they have been initially layouted (and set to their preferred size), the splitter cannot be moved! Both components have already reached their minimum. Fortunately, the solution is easy: Just make sure that the canShrink property of the percent layouts involved is set to true. In this case, the minimum size of the panels can be smaller than the preferred size. It is then possible to drag the splitter, at least until a "hard" limit is reached, e.g. the minimum size of a label.

Trees

Tree views are useful if any kind of hierarchical data is to be presented to the user, for instance a file system, a package structure, or the table of contents of a book. The model associated with the tree view must be capable of storing hierarchical data of these types. Popular UI libraries typically implement their own hierarchical data structure serving as a tree component's data model. Swing, for instance, uses the javax.swing.tree.TreeModel interface to define a hierarchical nodes structure. With the javax.swing.tree.DefaultTreeModel class a default implementation of this interface is available.

The JGUIraffe library takes a slightly different approach: Rather than defining another proprietary hierarchical data structure, the library makes use of an already existing one: the HierarchicalConfiguration class from the Apache Commons Configuration project. Because Commons Configuration is used for the implementation of the configuration support anyway, no additional dependency is required. The HierarchicalConfiguration class provides a rich API for querying and manipulating its data; all these methods can be directly used when working with tree views in a JGUIraffe application.

In Commons Configuration the addProperty() method can be used to add data to a HierarchicalConfiguration object. This method expects a key string pointing out the location of the new property in the hierarchical data structure. The string consists of several node names separated by the node separator character - which is the dot (".") per default, but can be changed to another character if needed. That way nodes are added to the configuration object. The tree view produced by the component builder tag library displays the node structure maintained by the HierarchicalConfiguration object. The nodes can also have values, but these values are not displayed. They can be easily queried, however. This can be used for instance to associate tree nodes with application-specific data; an event handler reacting on a change in the selection of a tree view could then access this data.

To make this clearer let's have an example. Have a look at the following lines of code that create a HierarchicalConfiguration object and populate it with nodes representing some classes of the JGUIraffe library:

HierarchicalConfiguration config = new HierarchicalConfiguration();
config.addProperty("net.sf.jguiraffe.gui.app.Application", Boolean.TRUE);
config.addProperty("net.sf.jguiraffe.gui.app.ApplicationContext", Boolean.TRUE);
config.addProperty("net.sf.jguiraffe.gui.builder.Builder", Boolean.TRUE);
config.addProperty("net.sf.jguiraffe.gui.builder.BeanBuilder", Boolean.TRUE);
config.addProperty("net.sf.jguiraffe.transform.Transformer", Boolean.TRUE);
config.addProperty("net.sf.jguiraffe.transform.Validator", Boolean.TRUE);
    

The fully-qualified class names are used as property names. Because the dot is the nodes separator character, the configuration creates corresponding node structures. The addProperty() method requires a value to be passed for each property to be added. Here we do not care about the value, so we just pass Boolean.TRUE. If we had some data to be associated with the class names, we would have provided it here. If now the configuration object initialized this way becomes the model of a tree view, results will look as follows:

Tree view example

After we have discussed the way the model of a tree view is represented by a HierarchicalConfiguration object it is time to explain how such components are actually created. The component builder tag library uses the <f:tree> tag for this purpose which is implemented by the TreeTag class. The tag supports a set of attributes for adjusting the resulting tree's appearance and behavior. Also the model of the tree must be specified through the model attribute. The following fragment from a builder script shows an example:

<f:tree name="myTree" model="treeModel" editable="false" rootVisible="true"
  multiSelection="false">
</f:tree>
    

The model attribute is mandatory. The string provided here is passed to the dependency injection framework to be resolved; it must refer to a bean of class HierarchicalConfiguration. It is up to a concrete application how this bean is created. One way would be to use the tags of the dependency injection tag library for this purpose. You can also create the configuration programmatically and put the object into the properties of a BuilderData object before you invoke the builder. This looks as follows:

ApplicationBuilderData builderData = application
        .getApplicationContext().initBuilderData();
HierarchicalConfiguration config = new HierarchicalConfiguration();
// populate the configuration
...
// add the object to the properties for the builder
builderData.addProperty("treeModel", config);

// invoke the builder
Window window = builder.buildWindow(locator, builderData);
    

Because a tree view is a more complex component the simple ComponentHandler interface is not sufficient to use all the features supported. Therefore, with the TreeHandler interface a specialized handler interface exists. TreeHandler provides methods for accessing specific properties of a tree and for manipulating its state and appearance. Using these methods the developer can

  • collapse or expand specific nodes of the tree
  • query or set the currently selected node(s)
  • obtain the model of the tree
  • register special event handlers for trees.

A specific node in the model of the tree view is represented by the TreeNodePath class. This class collaborates closely with a HierarchicalConfiguration object. It stores the configuration nodes on a path from the root node to a certain target node. It has methods to query the nodes on the path and supports transforming the whole path to a corresponding configuration key - this is especially useful if the data stored in the configuration for this tree node is required.

In many use cases trees are not used in forms to enter user data. Because of this, the data of a tree view component is per default not stored in the form's data; i.e. the noField attribute is initialized with true. This can be changed by manually setting the attribute to false. In this case the data stored for the tree view depends on the multi-selection property: If the tree supports only single selection (this means that only a single node can be selected at once), its data consists of a single TreeNodePath object. Otherwise, an array of TreeNodePath is stored.

Often it is required to use special icons for the nodes of a tree view. If not specified otherwise, the tree view uses a default icon for leaf nodes and another one for non-leaf nodes. JGUIraffe provides the TreeIconHandler interface which allows full control over the icons to be displayed. The basic idea is that an implementation of TreeIconHandler is specified using the iconHandler attribute of the <f:tree> tag (a bean with this name is looked up in the current bean context). The icon handler is passed the information available for the current node and returns the name of an icon to be displayed for it. The icons themselves must have been provided to the tree view using nested <f:treeIcon> tags (refer to the TreeIconTag class). An example of using this tag is shown below:

<f:tree model="myTreeModel" name="myTree">
   <f:treeIcon name="LEAF">
     <f:icon resource="myLeafIcon.gif"/>
   </f:treeIcon>
   <f:treeIcon name="BRANCH_EXPANDED">
     <f:icon resource="myExpandedIcon.gif"/>
   </f:treeIcon>
   <f:treeIcon name="BRANCH_COLLAPSED">
     <f:icon resource="myCollapsedIcon.gif"/>
   </f:treeIcon>
 </f:tree>
    

Here the default TreeIconHandler implementation is used. This implementation supports three different icons: for leaf nodes, for collapsed branch nodes, and for expanded branch nodes. The <f:treeIcon> tags define corresponding icons for these node types. The name attributes of the tags specifies the type of node for which an icon is to be set. Here the default names supported by the default icon handler are used. If you set a custom icon handler, you are free to use whatever icon names you want. The only caveat is that the names provided by the <f:treeIcon> tags correspond to the names supported by the handler.

Tables

Tables certainly belong to the more complex components that can be added to the UI of an application. They are typically used to display larger amounts of structured data. This makes them a bit similar to list boxes, but in contrast to the more simple list boxes tables can display an arbitrary number of columns. Therefore they require a different model.

List boxes obtain the data to be displayed from an object implementing the ListModel interface. This interface allows access to a value and a display object for each element of the list. This information is sufficient for a list box control to render itself and to maintain its data. For a table situation is more complex because the data to be displayed in the single columns is needed. This is the reason why interfaces representing models for tables typically have a bunch of methods. For instance, Swing's javax.swing.table.TableModel interface defines methods for querying data of the different cells of the table.

JGUIraffe takes a different approach. In fact, the table models used here are even simpler than list models: they are simple collections with Java beans. The JGUIraffe table implementation borrows some features from the implementation of Forms, especially the mechanism for accessing properties of the form's model- which typically is a Java bean - and binding their values to UI components. In the case of tables the properties of the Java objects stored in the model collection are mapped to the columns of the table. This makes it very easy to display the data an application is processing because in most cases no conversion to specialized model objects is necessary: Typically application data is available in collections of data objects anyway, e.g. as a list with entity objects representing the result of a database query. Such a list can directly be used as the model of a table.

For the definition of tables the component builder tag library provides the <f:table> tag which is implemented by the TableTag class. The columns of the table are defined by nested <f:column> tags (here the implementation class is TableColumnTag). The following example fragment from a builder script shows how a simple (read-only) table can be declared:

<f:table model="tabModel" selectionBackground="blue"
  selectionForeground="white" name="testTable">
  <f:column name="icon" width="20px" columnClass="Icon"/>
  <f:column name="firstName" percentWidth="35" headerres="TABLE_COL_FIRSTNAME"/>
  <f:column name="lastName" columnClass="String" percentWidth="40"
    headerres="TABLE_COL_LASTNAME"/>
  <f:column name="birthDate" columnClass="DATE" percentWidth="25"
    headerres="TABLE_COL_BIRTHDATE"/>
</f:table>
    

This script generates a table with four columns: an icon, first name, last name, and birth date. While the reference of all attributes supported by the tags is available in the Javadocs, we want to highlight some points:

  • Probably the most important attribute of the <f:table> tag is the reference to the table's model. A bean with the name specified here is looked up in the current bean context. It must be a collection.
  • In addition to the standard attributes supported by all input components the <f:table> tag provides some specific attributes for setting further properties of the table, e.g. the colors of the selection.
  • Each <f:column> tag defines a column of the table. The name attribute of the tag corresponds to the property of the beans in the table's model that is read for rendering this column. In the example above we deal with beans representing persons. Because there is a column named firstName the objects in the table's model must have a property named firstName, too - which means there has to be a public method with the name getFirstName() and no arguments that returns the data to be displayed in this column. Note that the actual class of the objects in the model does not matter; access to the data is done purely through the names of the properties.
  • Every column can have a header text (which is independent from the column's name). For defining the header text there are similar options as for Labels: the text can be set directly or by specifying a resource ID.
  • With the columnClass attribute information about the type of data to be displayed in that column can be provided. This gives a hint to the underlying table implementation how to render the data in that column. For instance, the icon column has set its type to ICON to ensure that an image is painted. Defining the column class is optional, but it is recommended to make sure that the table actually displays the data as expected. The following options are available:
    • The component builder tag library provides the ColumnClass enumeration class which defines a number of logic column types. Using the constants defined by this class is the most portable way of specifying a column class because JGUIraffe maps these types to the renderes supported by the underlying UI toolkit.
    • It is also possible to use a Java class as column class. In this case, the class is just passed to the underlying UI toolkit which must be able to handle it. For instance, Swing has built-in support for rendering a set of data classes.
    If no columnClass attribute is provided, a generic renderer is used for this column.
  • An important point for a good-looking table are well-defined column widths. The <f:column> tag supports both fixed and relative column widths. A fixed width is specified using the width attribute. Here a string compatible with the NumberWithUnit class can be provided. Such columns keep their width when the table is resized. Of course, the user can manually drag the columns' margins to change their size. With the percentWidth attribute a relative width can be set. When calculating the widths for the columns the total width of the table is determined, then the sizes of columns with a fixed width are subtracted. The remaining size is distributed to the columns with a relative width according to their percental ratio. Note that a table with at least one column with relative width always adjusts the size of its columns to fit into the available space. This is not the case for tables with only fixed-width columns: here the table can become wider or smaller than the space available. Columns with no width specification are assigned a relative width. The example script demonstrates a typical pattern for setting the column widths: Most data columns are assigned a relative width according to the size of the information they have to display. A fixed width is used only for special columns like the icon column.

Typically tables are used to display - and optionally edit - the data stored in its model. They do not store data themselves in the model of the current form. This can be changed by setting the noField attribute of the <f:table> tag explicitly to false. In this case the data stored by the table in the form's model depends on its selection model: If single selection is set, the index of the selected row is stored as an integer. Tables with multiple selection mode enabled (this can be achieved by setting the multiSelection attribute to true) store an array of integers for the indices of all selected rows.

Tables provide some specific functionality which cannot be accessed through the ComponentHandler interface. Therefore with TableHandler a specialized handler interface exists. This interface provides methods for querying and setting the table's selection, for accessing additional properties, and to notify the table about changes in its data model. This is important for making changes in the data model visable. Because the model of a table is a plain collection the table cannot figure out if an external component manipulates this data. It lies in the responsibility of the developer to pass notifications to the table by calling any of the corresponding methods (rowsInserted(), rowsUpdated(), rowsDeleted(), tableDataChanged()) provided by the handler interface. Among those methods tableDataChanged() is the most generic one. It causes an update of the whole table. The other methods are more specific - if you know exactly what has changed, it is more efficient to use them because then only the relevant parts of the table need to be updated.

If not specified otherwise, tables are read-only. This can be changed by setting the editable attribute of the <f:table> tag to true. In this case default editors are enabled for all columns of the table; the editors used depend on the data type of the associated columns. It is also possible to override the editable flag for specific columns. The <f:column> tag also supports an editable attribute. The default value of this attribute is the value of the editable attribute of the enclosing <f:table> tag. By setting this attribute for single columns, it is easy to turn on editing functionality on a per column base.

It was already discussed how the columnClass attribute can be used to influence the renderer or editor used for a column. However, sometimes the default renderers available do not fulfill all the requirements of an application. In this case there are multiple options.

In the introduction for tables we mentioned that the columns of a table behave in a similar way as the fields of a form: they can be bound to properties of the form's model. In fact this similarity goes a bit further because it is also possible to assign transformers or validators to a column. These objects work exactly the same for columns as for regular input components in a form. When a column is rendered the corresponding data value is obtained from the table model, and then the write transformer is applied to it. When a cell is edited the read transformers and the validator associated with the current column are invoked. So all the validation logic provided for Form objects is available to tables, too. In the listing below transformers are defined for some columns of a table:

<f:table name="table" model="tableModel" multiSelection="true">
  <f:column name="icon" width="20px" columnClass="Icon"/>
  <f:column name="name" percentWidth="50" headerres="main_tcol_name"
    columnClass="String"/>
  <f:column name="lastModified" percentWidth="30"
    headerres="main_tcol_modified">
    <f:transformer class="net.sf.jguiraffe.transform.DateTimeTransformer"
      type="write"/>
  </f:column>
  <f:column name="size" percentWidth="20" headerres="main_tcol_size">
    <f:transformer type="write"
      class="net.sf.jguiraffe.examples.tutorial.mainwnd.FileSizeTransformer"/>
  </f:column>
</f:table>
    

In this example a table is defined that displays information about the files in a directory. The columns for the icon and the file name use default renderers. For the date column the default DateTimeTransformer provided by JGUIraffe is used. The column for the file size is assigned a custom transformer.

The component builder tag library provides some more tags that allow an even more powerful customization of the renderers and editors used for the cells of a table. These tags basically associate a column with an arbitrary graphical component. This component can also be a panel with multiple nested input components, so that actually multiple properties of the table's model objects can be displayed in a single column. Have a look at the following example:

<f:column name="name" headerres="TABLE_COL_NAME">
  <f:colrenderer>
    <f:panel>
      <f:label text="First name:"/>
      <f:statictext name="firstName"/>
      <f:label text="Last name:"/>
      <f:statictext name="lastName"/>
    </f:panel>
  </f:colrenderer>
  <f:coleditor>
    <f:panel>
      <f:label text="First name:"/>
      <f:textfield name="firstName" maxlength="25"/>
      <f:label text="Last name:"/>
      <f:textfield name="lastName" maxlength="25"/>
    </f:panel>
  </f:coleditor>
</f:column>
    

Here a column for the name of a person is defined which actually contains two sub fields: the first name and the last name. Two new tags, <f:colrenderer> (implemented by the ColumnRendererTag class) and <f:coleditor> (implemented by the ColumnEditorTag class) are used to define the content of the column. In the body of these tags an input component tag can be placed, here a <f:panel> tag is used which is in turn a container for other input component tags. The panels have labels and text fields for the data to be maintained. In the case of the column renderer static text components are used because they are read-only per se. (Note that layout-related stuff was removed from the example to focus on the essentials; in practice a layout definition would be required.)

The component defined by the <f:colrenderer> tag is displayed per default when the table is just shown. When the user clicks into a cell it is replaced by the component defined by the <f:coleditor> tag. Sizing and layout of these components should be in sync to avoid disturbing effects when switching between these components.

Custom data type converters

The tag handler class we discuss in this sub section is a bit special because it does not create a graphical user interface element. Rather, it allows the registration of custom data type converters which can then be used by the dependency injection framework. The reason why it is part of the component builder tag library and not of the dependency injection builder tag library is because the latter is designed to require only a BeanBuilder for the execution of scripts. However, extended functionality for registering converters at the execution time of scripts is available for Builder implementations only. Note: It is possible to use custom data type converters with a BeanBuilder by passing in an InvocationHelper object which is configured accordingly. But this requires code to register converters at the corresponding ConversionHelper instance. With a Builder object converters can be defined in a script in a declarative way.

As is described in the data type converters sub section, converters are called by the dependency injection framework when an object defined in a builder script (e.g. a property or parameter value) has to be converted to a specific target data type. For basic Java types standard converters are available, but when an application operates on beans with special data types, registering custom converters can simplify bean declarations and help to make builder scripts less verbose.

In order to use a custom data type converter, the converter class has to be implemented first. This means that the Converter interface of Commons Beanutils has to be implemented. With the converter class in place, registering an instance is simply a matter of placing the <f:converter> tag (implemented by the ConverterTag class) at the beginning of a builder script.

ConverterTag is derived from the UseBeanBaseTag class. Thus it provides multiple ways of defining the converter object. For instance, if the converter class has a default constructor, it can be directly created by the tag as shown in the following example:

<f:converter class="com.mypackage.convert.MyCustomConverter"
  converterTargetClass="com.mypackage.model.MyBeanClass"/>
    

Here a converter instance of the class com.mypackage.convert.MyCustomConverter is created and registered for the target class com.mypackage.model.MyBeanClass. Alternatively the dependency injection framework can be used to create the converter instance as a bean which is then referenced from the <f:converter> tag. This approach is more powerful. If the converter has to be created using a different constructor or if properties have to be set, it is the way to go. The code fragment below demonstrates this scenario:

<!-- Definition of the converter bean -->
<di:bean name="customConverter"
  beanClass="com.mypackage.convert.MyCustomConverter">
  <!-- Further initialization, e.g. constructor calls or properties set -->
  ...
</di:bean>

<f:converter beanName="customConverter"
  converterTargetClassName="com.mypackage.model.MyBeanClass"
  converterTargetClassLoader="specialClassLoader"
  isBaseClassConverter="true"/>
    

This example shows some more options of <f:converter> tag. No matter which mechanism is used for creating the converter object, a target class must be specified. This is the class produced by the converter when converting objects. As can be seen here, the target class can either be specified directly or by providing its name and the name of the class loader to load the class (refer to the section Class loader issues for more details).

Further, the isBaseClassConverter attribute can be specified. If the attribute is missing, the converter will only be called if the target class of a conversion matches exactly the target class specified at the tag. If the attribute is present (the exact value does not matter), the converter will also be called for subclasses of the specified target class.

The converter is active directly after the execution of the <f:converter> tag. So bean declarations succeeding the tag can already make use of data conversions supported by the converter. When using the builder in the default way there is also a kind of inheritance mechanism for data type converters: The BuilderData object passed to the builder can contain a reference to the parent BeanContext. If this reference is set (which is the case per default), all data type converters active for the parent context are also available for the current builder operation. So depending on the builder script in which a converter is defined, its scope is determined. If an application uses some special data types for which custom converters are required, it can register the corresponding converters in its main builder script (this is the script defining the main window of the application, i.e. the script executed at application startup). They are then available to all builder scripts invoked later (provided that the default parent bean context is passed). On the other hand, converters that are very specific and are only required in a single builder script can be defined in this script only.

Components and the form model

In the sections about the single components supported by the component builder tag library it was also mentioned, which kind of data is stored in the form's model for this type of component. In this chapter we focus on this topic and show how to create a Java class that can serve as a model bean for a form.

Per default the model of a form is a plain Java bean. It is also possible to use other types of model objects as discussed in the section The binding strategy, but for many applications Java beans will be sufficient. The rules how the input components are bound to a Java bean are pretty simple: For a component with the name foo there must be a property in the bean with a corresponding data type that is also named foo (i.e. there must be public methods DataType getFoo() and void setFoo(DataType obj)). Instead of the name of the component the propertyName attribute of the tag defining the component can be used to specify the name of the model property. So if a builder script contains the following declaration:

<f:textfield name="fileName" displayNameres="newfile_disp_name"
  maxlength="200" propertyName="path">
</f:textfield>
    

the name of the property in the model bean would have to be Path. It is important that the data type of the property matches the type of data that is expected by the associated component - otherwise there will be exceptions when the data is accessed. The following table shows the mapping between components and data types in the model:

Component Remark Data type
Text components Without transformers java.lang.String
Checkboxes boolean or java.lang.Boolean
Radio buttons Depends on the ComponentHandler int or java.lang.Integer for the default handler
Lists/Combo boxes Single selection mode An object of the type declared by the list model.
Multi selection mode An array of the type declared by the list model.
Progress bars Per default not stored in the model int or java.lang.Integer
Sliders int or java.lang.Integer
Buttons Toggle buttons or command buttons (the latter are not stored in the model per default) boolean or java.lang.Boolean
Trees Single selection mode (per default not added to the model) An object of type TreeNodePath
Multi selection mode (per default not added to the model) An array of type TreeNodePath
Tables Single selection mode (per default not added to the model) int or java.lang.Integer
Multi selection mode (per default not added to the model) an array of int for the selected indices

Some remarks about this table:

  • Some components are typically not used directly to gather user input, but support editing by displaying further information. The table contains a remark for these components that they are not added to the model per default. If data for such a component is to be stored, the noField attribute of the declaring tag has to be set to false explicitly.
  • Properties are set or read through reflection. So in most cases it does not matter whether primitive types or wrapper types are used.
  • If Transformers are used, the actual data type can be different. In this case it has to be compatible with the type produced by the transformer.

What was not covered so far is the question how the model is associated with the form. When a builder script is processed a Form object is created and initialized automatically based on the components defined in the script. But the form does not manage its model on its own. This task lies in the responsibility of the Form controller. So in order to pass the bean acting as data model to the form the following steps must be performed:

  • A bean class with properties according to the table above must be created.
  • A bean declaration for this bean class has to be added to the builder script.
  • The builder script must contain the declaration of a form controller. Here the default implementation provided by JGUIraffe can be used.
  • A tag of the window builder tag library is used to connect the form controller with the window and to specify the model bean.

Form controllers and their declaration will be discussed in later chapters in more detail. For now we just provide a simple example that shows the minimum required steps for associating the form with its model.

In the section A simple example a simple form was generated with two text fields for creating new text files. A compatible model class could look as follows:

public class CreateFileData
{
    /** The file name. */
    private String fileName;

    /** The file content. */
    private String fileContent;

    public String getFileName()
    {
        return fileName;
    }

    public void setFileName(String fileName)
    {
        this.fileName = fileName;
    }

    public String getFileContent()
    {
        return fileContent;
    }

    public void setFileContent(String fileContent)
    {
        this.fileContent = fileContent;
    }
}
    

This is pretty easy because we have only two properties of type String, but you should be able to get the concept behind it. Now in the builder script we use tags of the dependency injection framework to create a bean of this model class:

<di:bean name="createFileModel"
  beanClass="net.sf.jguiraffe.examples.tutorial.createfile.CreateFileData"/>
    

Next the form controller is declared. It is associated with the buttons for saving or canceling the form.

<di:bean name="controller"
  beanClass="net.sf.jguiraffe.gui.builder.window.ctrl.FormController">
  <di:setProperty property="btnOkName" value="btnOk"/>
  <di:setProperty property="btnCancelName" value="btnCancel"/>
  <di:setProperty property="okCommand" refName="okCommand"/>
</di:bean>
    

The final step is the <w:formcontroller> tag which has to be placed inside the enclosing window. It establishes the connection between the form controller bean and the window to be controlled. It is also assigned the bean acting as the form's model:

<w:dialog ...>
  ...
  <w:formController beanName="controller" formBeanName="createFileModel">
  </w:formController>

  ...
</w:dialog>
    

Note that except for the creation of the model class all other steps are declarative. No further coding is required. JGUIraffe now handles the complete life-cycle of the form and ensures that the model is correctly filled.

This should be enough for now. Later chapters will provide more details about the associations between forms, form controllers, dialog windows, and form models. Also information about event handling is provided.