Building windows

So far we have discussed how UI elements can be created and positioned using layout managers. We also described methods for interacting with such elements, so that the UI can be manipulated when certain events are received or the user triggered specific actions. Now, the UI of an application does not live on its own, but is embedded inside a window. Windows are natural elements of modern graphical-oriented operating systems. Nevertheless they have to be defined somehow.

JGUIraffe contains another tag library that deals with windows: the so-called window builder tag library. The tags provided by this library can be used to define multiple types of windows:

  • Frame windows which usually represent main windows of an application. In many cases an application only has a single frame window which is the parent of all other windows created by this application. Frame windows are top-level windows of the operating system; for instance, they appear in the task bar.
  • Dialog windows are used to gather input from the user. They are typically child windows of the application's main frame window. They are rather local to the application and are not considered top-level windows of the operating system.
  • With internal frame windows there is another type of windows supported by the window builder tag library. These windows correspond to the Swing class javax.swing.JInternalFrame. They allow an application to open multiple child windows inside a main frame window, e.g. one child window for each document currently open. Such child windows were popular for instance in early versions of the Windows operating system; the approach was called Multiple Document Interface (MDI). Meanwhile state of the art applications do not use this concept any more. So it is recommended to avoid the use of internal frames. Also, they may not be supported by all platforms.

We will discuss the different window types and their creation in more detail in the following sub sections. But first we describe some of the basic interfaces used by the window builder tag library for the interaction with windows.

Fundamental concepts and classes

The creation of windows works similar to the creation of the other elements which have already been discussed in the previous chapters: They are defined using specialized tags in builder scripts. To use these tags it must be ensured that the window builder tag library has been added to the script. So the root element of the builder script must look as follows:

<?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">

</j:jelly>
    

Here - and in all following examples - we assign the prefix "w" to tags of the window builder tag library. Now tags like <w:frame> or <w:dialog> can be used to define the corresponding windows. We will come to these tags in more detail shortly. For now it is important to know that they act like container tags, e.g. the tag for creating Panels. This means that they can be assigned a layout by simply placing a corresponding layout tag in their body. Also, the tags for defining the actual UI - i.e. graphical elements like labels, text components, tables, etc. - can be nested inside such window tags. This is a pretty natural approach to defining windows which seamlessly continues the declaration of other parts of the UI.

The Builder interface defines a specialized method for processing a builder script that defines a window: buildWindow(). Like the other build methods it expects the locator to the builder script and the BuilderData object as arguments. It returns the main window defined by the builder script.

In theory, a builder script is free to define an arbitrary number of windows. In most cases however, only a single window - e.g. a dialog box - will be defined. This corresponds to a "unit" in a graphical application. In this case this single window is returned by the buildWindow() method. If a window is pretty simple and only used in the context of another window, it may make sense to define it together with the window it depends on in the same builder script. An example would be a simple message window that is displayed by a dialog box if the user enters invalid data.

In order to access multiple windows they must have been given unique names. This is achieved by specifying the name attribute at the tags that create the windows. (In contrast to tags for input elements, the name attribute is optional here.) During the builder operation there is an instance of the WindowBuilderData class which keeps track of the windows that are defined by the script. This instance also takes care that all windows can be queried from the current BeanContext using the reserved prefix window: followed by their name. Hence a code example obtaining a specific window could look as follows:

// Input for the build method
Locator scriptLocator = new ClassPathLocator("mydialog.jelly");
ApplicationBuilderData builderData =
  application.getApplicationContext().initBuilderData();
Builder builder = application.getApplicationContext().newBuilder();

// Process the builder script
builder.build(scriptLocator, builderData);

// Obtain the desired window
Window wnd = (Window) builderData.getBuilderContext().getBean("window:msgbox");
    

This example invokes the generic build() method on a newly created Builder instance. From the bean context created during this operation it retrieves the window with the name msgbox. Alternatively, the WindowBuilderData instance itself can be fetched from the bean context. It provides methods for querying windows created during the builder operation, too. If this approach is used, the last part of the example becomes:

...
// Obtain the desired window
WindowBuilderData wbd = (WindowBuilderData) builderData.getBuilderContext()
    .getBean(WindowBuilderData.KEY_WINDOW_BUILDER_DATA);
Window = wbd.getWindow("msgbox");
    

In most cases, if the builder script defines only a single window, the buildWindow() convenience method of the Builder interface should be used. It returns this window directly.

In JGUIraffe windows are represented by the generic Window interface. Compared with the APIs of other UI libraries this interface is very simple. It provides about a dozen methods for querying and setting properties of the underlying window. Typical JGUIraffe applications do not need more because the major part of the window's properties has already been set by the builder script. The methods defined by the Window interface are mainly intended to interact with the window at runtime. The following features are accessible through the Window interface:

  • The window can be opened. This is important for new windows created during a builder operation as they are not opened automatically. Calling this method performs necessary initializations and makes the window visible for the first time.
  • The counter part of open() is the close() method. It hides the window and frees all its resources.
  • The window's title can be queried and set.
  • The window's position on the screen and its width and height can be queried and set.
  • Event listeners can be added to and removed from the window. Event listeners are covered later in this document.
  • A window can be associated with a WindowClosingStrategy. Before the window is actually closed it asks its closing strategy for permission. Only if the closing strategy allows this operation, the window is closed. A typical use case could be a component that monitors whether the user has made unsaved changes on the data managed by the application. When the application's main window is to be closed and there is unsaved data, the closing strategy could display a message box asking the user whether the modifications should be stored. If the user now clicks on Cancel, the strategy would forbid the close operation so that the window stays alive.
  • Windows also can have a controller which can be queried using the getWindowController() method. Controllers are discussed in more detail later.

For many applications it is just sufficient to open and close a window. What happens in between is controlled by view and business logic which does not have to access the window object itself. Typically, interaction with UI controls is needed, but the window stays as it is after it has been created. Opening a window can also be automated by using the OpenWindowCommand class described in the section Actions and commands.

So far for the fundamental concepts of the Window builder tag library. We will now describe the tags for creating windows.

Tags for creating windows

As has already been pointed out, the Window builder tag library supports multiple types of windows. For each window type there exists a corresponding tag responsible for the creation of that window. The table below gives an overview over the tags and their associated window types.

Tag Implementation class Window type
<w:frame> FrameTag Frame windows
<w:dialog> DialogTag Dialog windows
<w:iframe> InternalFrameTag Internal frame windows

The good news is that all these tags are used in a very similar way. They are all derived from the abstract WindowBaseTag base tag handler class which already defines a set of attributes common to all types of windows. These attributes include

  • the position of the new window on the screen.
  • width and height of the new window.
  • the title of the window. It can be defined either directly as plain text or by a resource ID (in the usual way as is supported by many other tags of the JGUIraffe tag libraries).
  • some flags controlling UI-related features of the window, e.g. whether it can be resized, minimized, maximized, etc.
  • a flag whether the window should be centered on the screen.
  • the autoClose flag. This flag controls the behavior of the close button in the title bar of the window. If set to true - which is the default -, the window will automatically close itself (after it has asked its closing strategy). If set to false, the close button just causes an event to be thrown. The developer is then responsible for catching this event and reacting accordingly.
  • the closeOnEsc flag. This flag controls whether the window can be closed by pressing the escape key. This is usually desired behavior of dialog windows. Here pressing escape means the equivalent of pressing the dialog's cancel button: that the user wants to cancel editing and dismiss all changes. Therefore the flag is true per default for dialog windows and false for windows of other types. Note that a value of true for this flag assumes that a cancel button is present in the window (refer to Buttons for further information); actually, the escape key is handled by simulating a click event on this cancel button.
  • a reference to the menu bar of the window. Menus are defined using tags of the Action builder tag library as described in the section Main menus and tool bars. They are stored in the current context and can be referenced by windows.

In many cases the default values for these attributes are appropriate. For instance, the window's size is determined by its layout manager which calculates it based on the preferred sizes of the components contained. The position does not matter if the centered flag is set; this makes sense for dialog windows. So typical window definitions are indeed pretty simple. The following example fragment shows the definition of an application main frame window:

<w:frame titleres="main_title" menu="mainMenu" autoClose="false">
  ...
</w:frame>
    

Here we just define a title for the window (from a resource) and specify the menu bar. The menu has been defined using other tags in the same builder script. Examples for menu declarations and also for toolbars in windows can be found in the section Main menus and tool bars.

In this example the autoClose attribute is set to false. This can make sense in some special cases, e.g. if special cleanup is needed when the window is shut down. For this main window the reason for this attribute is a special event mapping: As was discussed in the section Mapping events to actions it is possible for actions to be triggered when specific events are fired. For the main frame of the application we have used this mechanism to map the window's closing event to the action that exits the application. If such a mapping is active, the auto close mechanism must be disabled because the close operation is already handled by the action. More details about this topic will be provided when we talk about window event listeners later in this document.

In order to define the window's content, tags from the component builder tag library can be placed inside the body of window tags. Each window defines an implicit panel acting as a container for arbitrary components. Make sure that the correct layout is set, so that the components are correctly arranged. For instance, a typical application main frame window might use a BorderLayout with a tool bar in the north and a panel with the actual content in center. A skeleton declaration for such a window could look like this:

<w:frame titleres="main_title" menu="mainMenu" autoClose="false">
  <f:borderlayout/>
  <!-- A tool bar at the top of the window's content -->
  <a:toolbar>
    <f:borderconstr name="NORTH"/>
    <!-- Content of tool bar omitted -->
  </a:toolbar>

  <!-- The main panel -->
  <f:tabbedpane name="tabs">
    <f:borderconstr name="CENTER"/>
    <!-- Content of main panel omitted -->
  </f:tabbedpane>
</w:frame>
    

The definition of a dialog window looks very similar. Here is an example:

<w:dialog titleres="newfile_title" center="true" resizable="true" modal="true">
  ...
</w:dialog>
    

Instead of the <w:frame> tag the <w:dialog> tag is used. Again, only a small sub set of the possible attributes is used. This time we specify a title, set the center flag which causes the window to be centered on the screen, and make it resizable. Whether a dialog window should be resizable or not depends on the controls it contains. If the controls do not profit from additional space (e.g. only small text fields or checkboxes are used), there is no point in allowing the user to change the size. In this case the default size calculated by the layout manager should be appropriate. If on the other hand the dialog contains text areas, lists, or other components that can grow with additional space becoming available, a value of true for the resizable attribute is suitable.

An attribute specific to the <w:dialog> tag is the modal attribute. As its name implies, it determines whether the resulting dialog should be modal. A modal dialog blocks all other windows of the application; they cannot be used before the dialog is closed. Usually dialogs for entering data are modal: they need to be filled before the application can continue with its normal execution. Non-modal dialogs in contrast are used when it makes sense to work with the application in parallel. A typical example is a search and replace dialog. While it is open it should still be possible to edit a text field in another window.

The tags for creating windows support icons. It is possible to place an <f:icon> tag in their body as described in the section Labels and icons.

That's it. This has been pretty much all important information about the definition of windows using the Window builder tag library. In the next sections we deal with some specialities related to the interaction with windows.

Window events and listeners

As is true for most UI libraries, JGUIraffe supports events related to windows and listeners that can receive those events. The event listener interface to be implemented by objects that want to be notified about state changes of a window is WindowListener. This interface defines a bunch of methods corresponding to changes in the life-cycle of a window:

  • the window was opened. This is the first event fired for a window. It is sent immediately after it has been opened.
  • the window was activated. An event of this type is fired each time the window gains the keyboard focus.
  • the window was deactivated. This is the counterpart of the window activated event. It is fired each time the window looses the keyboard focus.
  • the window was minimized to an icon. Racting on this event can make sense for instance if some of the window's resources can be freed that are not needed while it is in icon state.
  • the window has been restored from icon state. This is the counter part of the minimized event.
  • the window is about to be closed. This event is fired when the user clicks on the close icon in the window's title bar before the window is actually closed. If the window has been created with the autoClose attribute set to true, it will be closed automatically. Otherwise, this event can be caught by a listener which can then decide whether to close the window manually.
  • the window was closed. This is the final event sent for a window. It indicates that the window has been disposed.

All methods of the WindowListener interface are passed an object of the WindowEvent class. This object provides access to the source Window object affected by the event, the type of the event (this is an enumeration corresponding to the single methods of the WindowListener interface), and the original, platform-specific event object.

Listeners for window events can be registered at a window programmatically. For this purpose the Window interface defines the methods addWindowListener() and removeWindowListener(). (Note that in contrast to events related to form elements the FormEventManager class is not used for the registration of event listeners. FormEventManager only knows about elements contained in the current form. Also its specialized multiplexing features - e.g. registering an action listener for all elements providing this event - are not supported for windows.) The following example code fragment shows how a listener can be registered at a window created by a builder operation:

// Execute the builder script and obtain the main window
Window window = builder.buildWindow(scriptLocator, builderData);

// Create and register the window listener
WindowListener listener = new MyWindowListener();
window.addWindowListener(listener);
    

Here it is assumed that the MyWindowListener class implements the WindowListener interface. In addition to the manual registration of event listeners in program code, it is also possible to use a declarative approach: by defining window listeners in a builder script. This works similar to declarative registration of listeners at form elements as described in the section Declarative event listener registration: Basically, the bean representing the window listener is defined in the builder script. Then the <a:eventListener> tag is used to add the listener bean to the desired window. The window is referenced using the targetBean attribute:

<!-- Declaration of the window -->
<w:dialog name="myDialog" title="Test dialog">
  ...
</w:dialog>

<!-- The bean for the window listener -->
<di:bean name="windowListener" beanClass="com.mypackage.MyWindowListener"/>

<!-- Register the listener bean -->
<a:eventListener beanName="windowListener" targetBean="myDialog"
  eventType="Window"/>
    

As usual, the full power of the dependency injection framework can be used to initialize the window listener bean. For instance, references to required UI components or helper objects can be injected.

In the section Mapping events to actions a set of tags was described which are able to connect actions to events fired by form elements. With <w:windowEvent> (implemented by the WindowListenerTag class) a corresponding tag handler implementation exists for windows. Usage is analogous to the other tags for form elements: The tag is placed in the body of the tag defining the window. The name of the action to be associated with the window events has to be provided as an attribute. Optionally, an event filter can be specified, e.g. to map the action to a specific window event type. The following example fragment shows how the closing event of a window is mapped to an action. This is indeed an interesting use case. It allows the developer to specify that the application's exit action should be invoked when the user closes the main window by clicking on the close icon. That way code duplication can be reduced:

<!-- The main window -->
<w:frame titleres="main_title" menu="mainMenu" autoClose="false">
  ...
  <!-- An event listener that delegates the window closing event to the
       application exit action.
  -->
  <w:windowEvent actionName="exitAction">
    <a:eventFilter eventType="WINDOW_CLOSING"
      class="net.sf.jguiraffe.gui.builder.event.filter.TypeEventFilter"/>
  </w:windowEvent>
</w:frame>
    

Note that in this case the autoClose attribute of the <w:frame> tag was set to false. The window must not be closed automatically because the event listener may decide to veto the close operation - e.g. because there is still unsaved data. If everything is okay, the exit action will terminate the application. This also causes the main window to be shut down.

Controllers

In a classical Model View Controller (MVC) architecture, the controller plays an important role. It connects the model and the view and processes user input accordingly. In the chapter about Form controllers we have already discussed that JGUIraffe provides a fully functional controller implementation with the class FormController. This class can be used out of the box to control the life-cycle of a form: it populates the form's input fields with their initial values, performs validation of user input, and eventually updates the model with the data entered by the user. Only if more interaction is needed between the graphical elements of the form (e.g. if some input fields have to be disabled based on the values entered in other ones), a custom controller implementation (which should extend FormController) is required.

In the Form controllers chapter there was not much information about the creation and initialization of form controllers and how they can be associated with window objects. This is because a developer typically does not have to perform these steps manually. Rather, the complete initialization of a form controller is done by tags provided by the Window builder tag library.

In order to setup a controller for a window, a builder script has to contain the following declarations:

  • A bean declaration for the bean acting as the controller. The class of this bean must be FormController or a derived class. The full power of the dependency injection framework can be used to initialize all properties of the controller bean.
  • Of course, the window itself must be declared. This is done using one of the tags introduced in the Tags for creating windows sub section.
  • In the body of the tag defining the window the <w:formController> tag has to be placed. It must reference the controller bean. It is this tag which establishes the connection between the window and its controller.
  • Optionally, event listener registrations can be performed. This may be necessary for custom controller classes which need to track the status of some form elements.
We describe these steps in more detail in the remaining part of this sub section.

The first step is to define a bean for the controller. Here all features provided by the dependency injection framework as described in the chapter The dependency injection builder can be used. Because the base class for form controllers, FormController is fully functional, it can often be used directly instead of implementing a custom controller class. A typical bean definition for a controller might look as follows:

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

This declaration creates an instance of the base FormController class. (FormController has a default constructor, so it is sufficient to just specify the bean class.) On the new instance some properties are set:

  • The names of the buttons representing the OK and the Cancel button in the form. The controller instance will register itself as action listener at these buttons, so it is notified when the user wants to close the form.
  • As sdescribed in the sub section Processing of form data a command object is specified that is to be executed when the form is closed because the user has clicked the OK button (or performed a corresponding action). The command is a bean that must be available in the current bean context. Here we assume that a bean named okCommand is defined elsewhere in the script.

The properties set in this example declaration are probably the most important ones available for FormController beans. Especially the button names have to be defined in most cases so that the controller can react on button clicks appropriately. Command objects for the OK and Cancel actions are optional. They provide a convenient means for initiating complex processing of the data entered into the form. If no complex processing steps are required, you might prefer event listeners over command objects. Event listeners are directly executed by the event dispatch thread. So they should only perform short-running operations, otherwise the UI of the application will block. Advantages of event listeners are that they are easier to implement than a command object and that they are allowed to access the UI directly. (Access to the UI is only safe from within the event dispatch thread which executes the event listener objects.) The FormController class supports the event listener type FormControllerFormListener. Listeners of this type are notified when the form is closed. They can then react accordingly, e.g. save the data when the OK button was clicked. With the event listener tags introduced in the subsection Declarative event listener registration it is possible to create suitable event listener objects in a builder script and register them at a form controller. Given the form controller declaration above, an event listener of the class com.mypackage.MyFormControllerListener can be registered as shown in the following fragment:

<a:eventListener targetBean="controller" eventType="Form"
  class="com.mypackage.MyFormControllerListener"/>
    

This means that an instance of the class com.mypackage.MyFormControllerListener is created. This instance is passed to the addFormListener() method of the form controller bean referenced by the targetBean attribute.

FormControllerFormListener objects are always invoked when the form associated with the controller is closed. Often, an operation should only be performed when the OK button was clicked. One way to achieve this is to manually inspect the FormControllerFormEvent object passed to the listener: the type property of this event can be queried to find out whether the form was committed or canceled. An alternative approach is to map FormControllerFormEvents to an action and add a filter for the event type FORM_COMMITTED. (Refer to the sub section Mapping events to actions for more information about invoking actions through event listeners.) To do so, first an action - say okAction - must be defined in the builder script. Then the <a:customEvent> tag can be used to connect the action to the event listener. This is demonstrated in the code fragment below:

<!-- Definition of an action to be called when the form is committed -->
<a:action name="okAction" .../>

<!-- Register the action as listener at the controller -->
<a:customEvent actionName="okAction" targetBean="controller">
  <a:listenerType type="Form"
    listenerClass="net.sf.jguiraffe.gui.builder.window.ctrl.FormControllerFormListener"/>
  <a:eventFilter eventType="FORM_COMMITTED"
    class="net.sf.jguiraffe.gui.builder.event.filter.TypeEventFilter"/>
</a:customEvent>
    

After these declarations the form controller is in place and fully initialized - even with commands or event listeners for processing the data entered into the form. The next step is to associate the controller bean with the window it has to control. For this purpose the <w:formController> tag exists which is implemented by the FormControllerTag class. Typically, the tag is placed in the body of the tag declaring the window. This might look as follows:

<!-- The form bean. This object acts as the model of the dialog.-->
<di:bean name="createFileModel"
  beanClass="net.sf.jguiraffe.examples.tutorial.createfile.CreateFileData"/>

<!-- The dialog window -->
<w:dialog titleres="newfile_title" center="true" resizable="true">
  ...

  <!-- Connect the form controller -->
  <w:formController beanName="controller" formBeanName="createFileModel">
  </w:formController>
</w:dialog>
    

In this short fragment some interesting things happen. First a bean acting as the model of the form is declared. Per default, forms store their data in plain Java beans whose properties match the names of the form's input fields (refer to the sub section Components and the form model for more information). A typical pattern to obtain such a model bean is to simply declare it in the builder script. It can then be injected into all objects that need access to it, e.g. into the command object which is set as okCommand at the controller. This is a straightforward way to pass the form's data to the command which has to process it.

Then the declaration of a dialog window starts. The major part is omitted. We are only interested in the <w:formController> tag appearing in the body of the <w:dialog> tag. This tag looks pretty simple, but it plays an important role in the initialization of the controller. It performs the following steps:

  • A FormController instance needs access to some internal objects storing results of the builder process, e.g. the current ComponentBuilderData object. The tag initializes these objects, so the developer does not have to care about it.
  • The model of the form is passed to the controller. It is identified by the formBeanName attribute. The controller ensures that the data entered by the user is stored in this model object.
  • The controller object is stored in the window object so that it can be queried using its getWindowController() method.
  • The tag checks whether the form controller bean implements certain interfaces. If this is the case, corresponding registrations are performed automatically. The following interfaces are evaluated:
    • WindowListener. If the controller implements this interface, it is automatically registered as listener at the window.
    • WindowClosingStrategy. If the controller implements this interface, it is automatically set as the window's closing strategy.

After the execution of the <w:formController> tag the window and its controller are fully associated. The controller can then interact with the form autonomously without intervention of the developer.

There is still a small thing missing: In the section Additional validation support some classes have been introduced which provide feedback about validation errors to the user so invalid input can be detected immediately. These classes are called field markers because they are able to highlight an input field if it contains invalid data. In a JGUIraffe application there is a pre-defined default field marker which can be accessed from a bean context using the reserved name jguiraffe.fieldMarker. (Of course, an application can define a bean with this same name overriding the default marker.) For a field marker to work it typically has to listen for some events triggered by the controller, e.g. validation events or events reporting field status changes. It would be possible to use the standard event registration tags we have already discussed to register the field marker at the controller. However, this would be pretty inconvenient, especially as it is not per se known which concrete field marker implementation is active and which events it requires. Therefore a special tag exists which handles the registration of event listeners at a form controller. With this tag the form controller declaration from the last example can be rewritten:

  <!-- Connect the form controller -->
  <w:formController beanName="controller" formBeanName="createFileModel">
    <w:formControllerListener beanName="jguiraffe.fieldMarker"/>
  </w:formController>
    

Here the <w:formControllerListener> tag (implemented by the FormControllerListenerTag class) has been added. This tag must be placed in the body of a <w:formController> tag. It obtains the bean specified by the beanName attribute from the current BeanContext - in this case this is the default field marker implementation. Then it checks which controller-related interfaces are implemented by the bean. For each interface found the bean is registered as a corresponding listener at the controller. In our example the tag causes the default field marker to be registered for all required events fired by the form controller. This effectively enables highlighting of input fields containing invalid data.

This is all that has to be said about standard form controllers. Sometimes a controller has to interact with the UI in a special way to make it more dynamic. For instance, some input fields may only be enabled if certain conditions are fulfilled, e.g. if a checkbox is checked. In such cases the typical pattern is to implement a custom form controller class derived from the base FormController class. This class implements corresponding event listener interfaces in order to react on changes at the UI. For instance, to listen for the status of a checkbox, the FormChangeListener interface would have to be implemented. In the builder script tags would be placed for registering the controller as event listener at all required input components. The following example shows how a controller is registered as change listener at three checkboxes:

  <!-- Event listener declarations:
       The form controller is registered at some components to be notified for
       status changes.
  -->
  <a:eventListener component="filterTypes" eventType="CHANGE"
    beanName="controller"/>
  <a:eventListener component="filterSize" eventType="CHANGE"
    beanName="controller"/>
  <a:eventListener component="filterDate" eventType="CHANGE"
    beanName="controller"/>
    

In this example the referenced components are all checkboxes. Of course, the controller bean must implement the FormChangeListener interface. It is then invoked whenever one of these checkboxes changes its value and can update the UI correspondingly.

This concludes our description of windows and their handling in the JGUIraffe library. You should now be able to define windows, associate them with controller objects and gather and process user input.