Aside: Special Properties of Composites

To make the conversion of composites more flexible, the following special properties of composites are always read, when the base key is not directly mapped to a value:

1) The instance property gives the user control over what instance is created and subsequently configured. The instance property is converted with the same converter as the composite. There are three uses for this property.

The first use is to use another resource bundle key as a template. Since resource bundle key conversion normally results in a new object being created, the instance property can first reference another resource bundle key which has various properties configured. A new instance of the composite will be created from the template, then its properties can be modified.

For example, an image or multimedia application might have controls for the amount of red, green, and blue in a picture. The amount of each color can be controlled by a slider, which has values ranging from 0 to 100. The slider is also to show tick marks. Rather than writing the code for three very similar sliders, a template slider can be defined that has most of its properties set to their desired values for all three sliders. Then each slider can reference the template slider using the instance property. Because instance property points to a resource bundle key, each slider is an independent object. Each slider then has its value property set according to the color the slider controls.

Here's a resource bundle with the code implementing the above description:
slider.min=0
slider.max=100
slider.value=50
slider.majorTickSpacing=10
slider.minorTickSpacing=2
slider.paintTicks=true
# Reference the "slider" resource bundle key as a template
redSlider.instance=%slider

# Assume redValue is mapped to an Integer in the argument map
# ":rw" means read the initial value from the map, and upon 
# user change, update the map
redSlider.value=$redValue:rw

greenSlider.instance=%slider
greenSlider.value=$greenValue:rw

blueSlider.instance=%slider
blueSlider.value=$blueValue:rw
Incidentally, literal strings can also used as templates.

The second use of the instance property is to have a BeanShell script (see Integration with BeanShell) determine what object is actually created. This might be a subclass of the component type that the text2gui library is expecting to create. For example, consider the creation of a subclass of JLabel, DisappearingLabel whose text disappears after a delay:
dLabel.instance=<{
   delay = 5000; 
   return new foo.bar.DisappearingLabel(delay);
}>
dLabel.text=This will disappear if not set again soon!
dLabel.hAlign=left
Because it ends with "Label", DispatchingComponentConverter expects that the dLabel key will describe a JLabel (see Class ID Guessing in DispatchingComponentConverter for details). But the instance subkey returns a subclass of JLabel, DisappearingLabel, which is configured as usual with the text and hAlign properties. Also, we could have performed some code before returning the instance. Note that the code that converts the resource bundle key dLabel does not need to know anything about DisappearingLabel when it is compiled.

The third use of the instance property is to configure a composite object passed into the argument map. A custom component not creatable by the text2gui converters might be created by the application, then put in the argument map. Say that this component is a disappearing label, as above, and is put as the value of the disLabel argument map key with the call:
argMap.putNoReturn("disLabel", new DisappearingLabel(5000));
Then the resource bundle might have the following definition:
dLabel.instance=$disLabel
dLabel.text=This will disappear if not set again soon!
dLabel.hAlign=left
The text and hAlign properties of the label passed in will be set when the resource bundle key dLabel is converted to a component. The result of conversion is the same label passed in, although its properties are modified.

Sidenote: Components passed into the argument map can be used directly without configuration, as in:

panel.contents=[$disLabel, %someOtherComponent]

2) The preInit property provides a chance for a BeanShell script to execute after the composite is created, but before the properties of a composite are set. The property is converted with a special instance converter. If the property does map to a BeanShell script, the variable me will be set to the composite object in the BeanShell environment. The result of the instance converter is discarded.

A typical use would be to create a button group for the children of a panel:
panel.preInit={
  putGlobal("bg", new ButtonGroup(), argMap);
}
panel.layout=grid cols=2
panel.border=titled title="Select a position:"
panel.contents=[%onTopRadioButton, %onBottomRadioButton]

onTopRadioButton.text=Missionary
onTopRadioButton.buttonGroup=&bg

onBottomRadioButton.text=Cowgirl
onBottomRadioButton.buttonGroup=&bg
In the above example, a new button group is created and put into the global namespace using the preInit property. Because the preInit property is converted before the rest of the properties, the global variable bg is guaranteed to be available by the time the contents property is converted. Thus the bundle keys used to create the contents may safely reference bg.

3) The postInit property provides a chance for a BeanShell script to execute after all properties of a composite are set. The property is converted with a special instance converter. If the property does map to a BeanShell script, the variable me will be set to the composite object in the BeanShell environment. The result of the instance converter is discarded. Typically, this property is used to configure the composite in a way that cannot be done by setting its properties, or to notify some other object with the fully configured created composite.

The following properties file demonstrates the use of the postInit property to ensure that the last entry of a JList is visible. This cannot be done by setting properties, so it needs to be done with a script.
scrollPane.viewportView=%list
scrollPane.prefSize={width=150, height=100}
#Need to do this here instead of the postInit of the list because the
#list needs to be in a viewport for this to work. At the time of
#creation of the list, it's not in the viewport yet.
scrollPane.postInit={
  me.getViewport().getView().ensureIndexIsVisible(9);
}

list.listData=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Only after the list is installed in the scroll pane, does JList.ensureIndexIsVisible() method have any effect, so the method is called after the scroll pane is fully constructed, in the postInit script.