Goals
The first and foremost goal of the text2gui package is to make Swing
application programming easier, especially for programmers that
write internationalized (i18n) applications. Without the text2gui
package, a large amount of tedious code needs to be written to create
and configure components, or a clumsy GUI builder tool needs to used
that either inserts code into your source files (how dare they touch
your code!), or stores components in a form indecipherable by humans
(serialized form).
If component creation and configuration is done with compiled code,
then whenever the
configuration of a component needs to be changed, the source file for
the GUI needs to be recompiled, even for something as minor as changing
the spacing between two components. The edit - compile - run - edit
cycle wastes a lot of programming time and effort.
Furthermore, the
code itself is a big mess, littered with tons of references to
components that might have their properties changed. Instead of a
single object that changes the properties of the GUI, there is one
object for each small part of the GUI. It is a maintenance nightmare.
Regarding the use of serialization, see
Problems with Using Serialization
for Component Creation.
As we will see, the "code" that describes a GUI can be specified by a
more natural and compact language compared to Java. This is
because GUI's are basically containment hierarchies; frames contain
panels which contain buttons, etc. Java is more or less a procedural
language which is also good at describing implementation hierarchies,
but clumsy at describing containment.
Java's strong type system is also
a significant hindrance to writing GUI code. Pesky casts are needed
everywhere or else the code fails to compile. Also, exceptions must be
caught and handled, even if the programmer is confident that no
exception can occur, or if no ill effects would result from ignoring an
exception.
The core of an
application that uses the text2gui library is still implemented in
ordinary Java, but the GUI is specified with a different, simpler
language more suitable for describing GUI's. This language is
interpreted during runtime at minimal cost by a small Java library, so
no recompilation is necessary.
The second goal of the text2gui package is to standardize the interface
between
GUI
model and GUI
view / controller code. This
allows the application state and the GUI implementation to be
separated. Programmers specializing in either part may largely ignore
the implementation details of the other part. The result of this
separation is simpler, more maintainable application code.
Also, this
separation allows the implementation of the GUI
to be upgraded
or chosen from a set, without forcing the user to download the entire
program again. Compared to simply
changing the Look and Feel, much more radical changes are possible --
the choice, properties, behavior, and arrangement of
all components can change.
How Does It Work?
The text2gui package accomplishes these goals with the following
mechanisms:
- Conversion from strings to arbitrary objects is made simple and
dynamic. These strings conform to a simple, compact syntax.
- Strings are provided by resource
bundles, which are loaded at runtime. This allows strings to be
stored outside of the program text, and also provides a way to
automatically load different strings depending on the locale. These
different strings, in turn, can specify different objects. Resource
bundles are backed by text files which are easy to understand and edit
by human programmers.
- Observable maps (called argument
maps) provide a means of communication between GUI model and GUI
view / controller code. To change a property of a component, the key
corresponding to the property is mapped to a different value. To
receive notification of a user input event, an application adds a
property change listener to the argument map. When a user input event
occurs, the argument map is notified of the event, and in turn,
notifies the corresponding property change listener.
Preview of text2gui Code
Want to see what text2gui code looks like before you start? The
following is a text file which describes the component hierarchy for a
slide show application:
mainFrame.title=Slideshow Demo
mainFrame.contentPane=%contentPanel
mainFrame.windowListeners.0={
new WindowAdapter() {
public void
windowClosing(WindowEvent event) {
System.exit(0);
}
}
}
contentPanel.layout=box axis=y
contentPanel.contents=[
{jscrollpane
viewportView=%pictureLabel},
%controlPanel
]
pictureLabel.icon=$icon
pictureLabel.border=etched
pictureLabel.hAlign=center
controlPanel.layout=box axis=x
controlPanel.contents=[{glue},
%indexLabel, {strut},
%indexField, {strut length=20},
%backButton,
%forwardButton, {strut length=20},
%randomCheckBox, {glue}
]
controlPanel.maxSize={width=1200,
height=50}
indexLabel.text=Picture Index
indexField.dispatchType=jformattedtextfield
indexField.value=$pictureIndex:ruw
indexField.prefSize={width=50,
height=20}
indexField.maxSize={width=75,
height=50}
backButton.icon.location=Back24.gif
backButton.pressed=$back:w
backButton.tooltip=Go to the
previous picture
forwardButton.icon.location=Forward24.gif
forwardButton.pressed=$forward:w
forwardButton.tooltip=Go to the
next picture
randomCheckBox.text=Randomize
order
randomCheckBox.selected=$random:rw#true
The following is the Java code that creates the component hierarchy,
controls it, and responds to user input:
/** A demo slideshow application that displays pictures of Lily. */
public class SlideShow extends AppBase
implements PropertyChangeListener {
/** Respond to user input. */
public void propertyChange(PropertyChangeEvent event) {
String propertyName = event.getPropertyName();
Object value = event.getNewValue();
if ("pictureIndex".equals(propertyName)) {
int index = ((Number) value).intValue();
if ((index >= 0) && (index < _images.length)) {
_pictureIndex = index;
_argMap.putNoReturn("icon", _images[index]);
}
} else if ("back".equals(propertyName)) {
_changePictureIndex(-1);
} else if ("forward".equals(propertyName)) {
_changePictureIndex(1);
}
}
public static void main(String[] args) {
start(new SlideShow(), args);
}
protected INoReturnObservableMap _makeArgMap() {
// Creates the argument map used to control the GUI: "_argMap"
super._makeArgMap();
// Put initial property values in the map.
_argMap.putNoReturn("pictureIndex", new Integer(0));
_argMap.putNoReturn("icon", _images[0]);
_argMap.putNoReturn("random", Boolean.TRUE);
return _argMap;
}
/** Register this instance as a listener of 3 user input events. */
protected void _addArgMapListeners() {
_argMap.addPropertyChangeListener("pictureIndex", this);
_argMap.addPropertyChangeListener("back", this);
_argMap.addPropertyChangeListener("forward", this);
}
/** Update the picture index by the increment, or if we want to get a
* random index, generate a new picture index. Update the GUI
* appropriately.
*/
protected void _changePictureIndex(int increment) {
if (MapUtilities.getBooleanValue(_argMap, "random")) {
// Generate a NEW index
int pictureIndex = -1;
do {
pictureIndex = _random.nextInt(_images.length);
} while (pictureIndex == _pictureIndex);
_pictureIndex = pictureIndex;
} else {
_pictureIndex = (_pictureIndex + increment) % _images.length;
if (_pictureIndex < 0) {
_pictureIndex += _images.length;
}
}
// Update the GUI
_argMap.putNoReturn("pictureIndex",
new Integer(_pictureIndex));
_argMap.putNoReturn("icon", _images[_pictureIndex]);
}
protected int _pictureIndex = 0;
protected Random _random = new Random();
protected Icon[] _images = new Icon[] {
new ImageIcon("lily1.png"),
new ImageIcon("lily2.png"),
new ImageIcon("lily3.png"),
new ImageIcon("lily4.png"),
};
}
Because this class extends
AppBase,
the actual code to do component creation is not shown above. It is
performed in
AppBase with the
one-liner:
_frame = (JFrame)
DispatchingComponentConverter.DEFAULT_INSTANCE.toComponent(
_bundle, "mainFrame", _argMap);
Finally, here's a screenshot of the result:
Changing the picture index or clicking
the back or forward buttons changes the image.
The important points to notice:
- The Java code is simple and clean. It only has one purpose: as a
model of the GUI state.
- The component hierarchy definition is compact and easily
understood
- The Java code is almost independent of the component hierarchy
definition. It doesn't need to know anything about buttons, text
fields, scroll bars, etc. The component hierarchy can change
considerably without requiring recompilation of the Java code.
- The Java code receives user input events by implementing a single
type of listener: property change listener
- The Java code changes properties of the view by associating keys
with values in a map
- The English text in the component hierarchy definition can easily
be replaced with text from
another language
Next Steps
Start learning how to use the text2gui library in the
Basics. There are many pieces of detailed
documentation, but don't let this discourage you. You don't need to
know all of the details before writing your own component hierarchies
for text2gui. We're confident the time you spend learning how to use
the text2gui library will pay off big in terms of the time you save
writing GUI applications, and the improved quality of your applications.