Introduction
As the culminating project for last semester’s Java programming class, I demonstrated how to emulate the features found in Windows Notepad in a Java application. Most of the algorithms were routine as the Java API is pretty robust. The one glaring omission in the API is a dialog analogous to the JFileChooser
or JColorChooser
to choose a font. Rather than omit the ability to choose a font, I chose to implement it myself.
The requirements for the font dialog were straightforward:
- The dialog must display all the available fonts on the user’s system.
- The user must be able to manipulate the font’s family, style, and size.
- The implementation must include a preview of the font.
- The implementation must expose a font object for use elsewhere.
Using the code
Layout the form
The code for the GUI aspects of the dialog is contained within the initComponents()
method. I used a BorderLayout
as the main layout for the form. The center component is a JPanel
which uses a GridLayout
manager. The GridLayout
itself contains two JPanel
s – one for the user controls (fontPanel
) and one for the preview (previewPanel
). The user control panel uses the GridBagLayout
manager and the preview panel uses a BorderLayout
. The south component of the main BorderLayout
contains the third significant panel for the buttons on the form (buttonPanel
), and it uses a FlowLayout
with the components aligned on the right side of the panel. All of the code is available in the code listing.
The initComponents()
method also does a number of other things: it declares and initializes the components, and creates anonymous inner listener classes for each of the controls that need them. I used three JList
components as the user controls in the dialog – one to list the available font families (lstFont
), one to list the available styles (lstStyle
), and one to list the potential sizes (lstSize
). It also positions the dialog in the middle of the user’s screen upon creation.
The code that initiates the font family list box is the only noteworthy code in the initComponents()
method:
lstFont = new javax.swing.JList(
GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames());
The above code queries the system for all of the available fonts, and loads them in the list box, satisfying the first requirement of the dialog.
Handling the change events
The three JList
change event handlers all share a common structure. Each event handler first creates a new font object based on the current state of the list boxes. They then assign the newly created font object to an internal font variable. Finally, the font of the preview label is set to the internal font variable. This ensures that any call to getFont()
is in synch with what the dialog displays to the user. This satisfies the second and third requirements of the dialog.
The architecture
The architecture for the form is pretty simple. I used a private font object that is manipulated through change events of the three list boxes – one for the font’s family, one for the font’s style, and one for the font’s size (see figure 1). The font object is immediately reflected in the preview panel, and is exposed by a public getFont()
method. Calling applications can make use of the getFont()
method to set the font of any object. This satisfies the fourth requirement of the dialog.
The constructors
One of the more common ways to implement a dialog that manipulates the properties of some other control is to include a reference to the component in the constructor(s) of the dialog. It’s customary to include references to as generic a superclass as possible in each constructor so that more subclasses can make use of the dialog. You’d then instantiate the dialog with a reference to the component to be changed and change the property when the user clicks the “OK” button. The dialog would then dispose off itself. This makes sense when there are only a couple of different types of components that make use of the dialog. In the case of the JFontChooser
, I decided that this was too much work.
A second way to implement the dialog is to de-couple the dialog from the component and get the properties from the dialog after the user is finished with it. That’s the method I used. The caveat, of course, is that the calling application is responsible for disposing the dialog after the program is done with it. This seemed like a fair trade-off, and it makes sense that the application that creates the dialog needs to destroy it as well. Sure, the dialog will be reclaimed by the garbage collector, but it’s a good habit to free unused resources as soon as possible in any program.
I created four constructors to handle any combination of two components – a JFrame
parent and a Font
object. The zero-argument constructor exists so that I can make the dialog a JavaBean if I choose to later on. The other constructors handle the possible use scenarios. Of course, the most useful constructor is the one which expects both a JFrame
and a Font
. That’s the one that allows you to reflect the current font of the component you are allowing the user to manipulate.
Using the dialog
Using the JFontChooser
is pretty straightforward:
- Instantiate and show the dialog.
- Determine if the user clicked the “OK” button.
- If the “OK” button is selected, change the property of the component – otherwise, do nothing.
- Dispose off the dialog.
The code for using the JFontChooser
might look something like this:
JFontChooser fd = new JFontChooser(this,txtDoc.getFont());
fd.show();
if(fd.getReturnStatus() == fd.RET_OK){
txtDoc.setFont(fd.getFont());
}
fd.dispose();
The above assumes that the call is made from a JFrame
and that txtDoc
is a text component with the corresponding getter and setter methods for its internal font. The dialog is not, however, limited to text components – you’re free to use it with any component that expects a Font
object as a property. Note that getReturnStatus()
will only return RET_OK
if the “OK” button is clicked.