This article presents a solution to this problem, in the form of a GUI library called the 'justnuff' library, specifically designed for non-GUI developers. We will first present the design and usage of the library, emphasising the ease of use for those unused to creating GUIs. Next month we'll look under-the-hood and reveal the code of the library, exploring many advanced new Swing-based GUI features, and discuss some interesting techniques for GUI library design.
The virtue of doing justnuff GUI
Essentially, when one is programming in the Java language, one is faced with a dilemma: When Java programming, you're either doing GUI or you're not.GUI is an all-or-nothing phenomenon in Java programming. This may be attributed to the language's origin as a web page graphical applet creation facility. Both the initial java.awt.* GUI library, and the now standard Java Foundation Classes (JFC or more frequently called "Swing") library has a completely GUI-centric programming view – one that demands a steep learning curve and an even longer mastery cycle.
Figure 1: The GUI-centric model of Java's GUI libraries
In Figure 1, which illustrates the GUI-centric model of Java's GUI libraries, the GUI is the primary focus of application design, and application logic is secondary. The user interacts with the GUI of the application, and events are generated based on the interaction (i.e. user clicks a button or selects a menu item) and are placed into an event queue. This event queue is processed by a single event handler thread (in the case of the older awt library, this is actually co-ordinated through a "peer" object in the host's operating system), and the GUI is updated or certain application logic is executed. Note that while the single event handler thread is performing the application logic, it is not available to update the GUI. This means that any application logic taking a significant amount of time may "lock up" the GUI – making it irresponsive to the user. This further aggravates the already second-class citizen status of application logic in this model. The typical recommendation is to spawn another "application logic thread" to perform any application logic that may require significant processing time. However, for developers who are focused on their application logic, and do not want to spend valuable productive time fiddling around with complex GUI APIs, the operation model depicted in Figure 2 is ideal.
Figure 2: justnuff approach to GUI
In this model of operation, the application logic is taking centre stage. The application logic executes in its own thread, completely free of any GUI encumbrances. When (and only when) it needs to display status to the end user, or to obtain input from the user, it will go to the GUI subsystem to get it done. The GUI system still runs its own event handling thread, but the setup and maintenance (the hard part) is handled by our justnuff library. Whenever the application logic calls for a GUI operation, it will synchronise and wait until that operation is completed. This is done transparently, by the justnuff library. What happens is that an event is queued in the event queue of the GUI subsystem, and the application logic's thread will wait until the event is processed before continuing on. This effectively converts a highly asynchronous and object-oriented GUI subsystem into a synchronous "flat" API – greatly facilitating the "just enough" usage pattern.
Requirements of the justnuff library
In order to make the justnuff library user-friendly, the following are the essential user requirements – adopting the view of a typical non-GUI programmer.- The API library must be easy to learn, use, and master.
Since mastering sophisticated GUI API programming is not the primary goal in life for our user base, it is unacceptable for the library to take days to learn and master (unlike swing and awt). The goal for the justnuff library is a 5-minute learning curve and one hour to mastery time. This will ensure that our user base can focus on the work at hand and not be distracted by GUI programming details.
To achieve this requirement, the API must be highly regular and simple to remember. Care must be adopted for API naming, parameterisation, and calling convention. We must not assume any prior swing, awt or GUI programming experience.
- All library APIs must be callable from an application logic thread.
This is obvious from Figure 2. Since the application logic thread must be able to call into the GUI subsystem at any time, without running into potential thread safety problem. This may sound simple, but it turns out that over 97% of the swing's API methods are unsafe to call directly from another thread! Swing is not thread-safe.
This is the main reason why we need to synchronise the application with the GUI's event handling thread through the event queue in Figure 2. This approach will ensure that any GUI manipulations are always handled by the GUI's one and only event handling thread – thus satisfying the thread-safety requirements of the swing API. This level of complexity must not be exposed to the end user.
- The API calls must be stateless.
Tracking and managing complex GUI states is the main contributor to GUI programming complexity. In order to greatly reduce programming complexity for the justnuff user base, we will need to keep the API free of programmer visible states. This means that a typical library user can simply call a GUI method to interact with the user, without having to worry about initialisation, cleanup, or dependency on any other API calls.
- The library should not create too many large dynamic objects.
CPU cycles and memory resources are best spent on the application logic – the main component of jusnuff applications – instead of the GUI subsystem. In order to ensure this, the library coding must be careful not to create too many large dynamic objects during its operation – taking up valuable memory space and eventually triggering CPU intensive garbage collection.
To satisfy this requirement, the justnuff library is specially designed and coded to re-use singleton GUI objects whenever possible.
- The library must be extensible.
We only have space and time to discuss a minimal library implementation here. Users may find it necessary to customise some of the available GUI facilities for their own needs. This requirement will ensure that the user can add new functionality to the library easily.
- The library should be utilising some of the latest GUI features.
Using the latest GUI facilities enables an application to appear current and relevant to the end user of the application. Judicial use of new features can also greatly simplify the design and coding of the library. A few JDK 1.4+ new GUI features really make life simpler; we will be using them in the coding of the justnuff library.
Using the justnuff library
Using the justnuff library is very simple by design. Take a look at the JustnuffTest.java, the test program for the library. It is annotated below to show how to use the library.You only need one single import statement for the library, highlighted below:
import uk.co.vsj.ui.justnuff.*;
import java.util.Date;
public class JustnuffTest {
Every justnuff API is a method of the UISystem class. The UISystem is a singleton (one instance only in the application), and you use the static getInstance() method to get a hold of the instance.
private static UISystem justnuff = UISystem.getInstance(); public JustnuffTest() {}Our test program's logic is in the static main() method of JustnuffTest.
public static void main(String[] args) throws Exception {
Status Display modal dialogs
The first call is to show an information dialog box.justnuff.showInformationModalDialog( "This is the justnuff UI library");The user clicks the OK button to dismiss the dialog. The application call is blocked until the displayed GUI dialog is dismissed. Figure 3 shows the information dialog displayed.
Figure 3: Information dialog
The next call is to show a warning dialog box.
justnuff.showWarningModalDialog( "Please be careful!");The displayed warning dialog is shown in Figure 4.
Figure 4: Warning dialog
The next call is to show an error dialog box, and the behaviour is similar to the information and warning dialogs.
justnuff.showErrorModalDialog( "An internal error has occurred.");The call to showYesNoCancelModalDialog is slightly different to the others because it returns a value indicating which button is pressed. It can be either UISystem.YES, UISystem.NO, or UISystem.CANCEL.
int tpCode = justnuff. showYesNoCancelModalDialog( "Do you understand this message?"); switch (tpCode) { case UISystem.YES: justnuff.showInformationModal Dialog("You have selected the YES option."); break; case UISystem.NO: justnuff.showInformationModal Dialog("You have selected the NO option."); break; case UISystem.CANCEL: justnuff.showInformationModal Dialog("You have selected the CANCEL option."); break; }Figure 5 shows the dialog that is displayed and its three buttons.
Figure 5: Resulting dialog
Data input dialogs
The next fragment displays a dialog requesting the user to enter a text value, in this case her/his name.String myInput = justnuff.showTextInputModalDialog( "Enter your name:"); justnuff.showInformationModalDialog( "You have entered a name of " + myInput );Figure 6 shows this input dialog.
Figure 6: Input dialog
Once a value is entered, the showInformtionModalDialog() call is used to display the value that the application receives.
The next call, showNumericInputModalDialog, enables a user to enter a long numeric value. Unlike the text variation above, this dialog will return either a valid numeric value or 0.
long myNumber = justnuff.showNumericInput ModalDialog("Enter the number of times to repeat:"); justnuff.showInformation ModalDialog( "You have requested repeat of " + myNumber + " times.");The showDateInputModalDialog() enables input of date information in a format similar to 12/1/2004. It will return either a valid Date object or null.
Date myDate = justnuff.showDateInputModal Dialog("Enter the date you started work:"); justnuff.showInformationModalDialog( "You have entered the date " + myDate);The showPasswordInputModalDialog() provides for input of password information. The password text is not echoed.
String myPass = justnuff.showPasswordInput ModalDialog("Enter the password:"); justnuff.showInformationModalDialog( "You have entered the password " + myPass);Next, the showPhoneInputModalDialog() method accepts input of a phone number in the format: (505) 555-5555. Unlike the other dialogs, this one will actually enforce the entry of the data in the required format. The return value is either a valid phone number or null.
String myPhone = justnuff.showPhoneInputModal Dialog("Enter the phone number:"); justnuff.showInformationModalDialog( "You have entered the phone number " + myPhone);Figure 7 shows the phone dialog at work. Note the enforced data entry format.
Figure 7: Phine number entry dialog
The next and last method in the justnuff library is the showObjectInputModalDialog() method. This is a powerful variation of the input dialog. It can take an object as one of its arguments and then display a dialog box that enables input to each public field member of that object. The version in our sample code supports string-typed members, long-typed members, and phone-typed members. You can modify the code to handle additional type members that you may need. The sample code version also has a limit of up to 5 field members.
Object myObj = new BankAccount(0,"","","", 0); Object myObj2 = new BankAccount2("","","",0); Object myRet1 = justnuff.showObjectInputModal Dialog("Enter values now:", myObj); System.out.println(myRet1.toString()); myRet1= justnuff.showObjectInputModal Dialog("Try values now:", myObj2); System.out.println(myRet1.toString()); ...Figure 8 shows the first dialog that is displayed for inputting a BankAccount object.
Figure 8: First dialog for bank account input
The BankAccount object is defined with the following fields.
public final class BankAccount extends Object { public long accountNumber; public String firstName; public String lastName; public String phoneP; public long balance; ...Note that the phone field's name ends with a capital letter P. This is an indication to the showObjectInputModalDialog() method that this string field represents a phone number.
The final segment of code tests the showObjectInputModalDialog() method when an object either has a field type that cannot be handled, or has more than 5 field members. An error dialog is presented instead of the input dialog in this case.
Object badObj = new TooLargeBankAccount( 0,"","","", 0,0); justnuff.showObjectInputModalDialog( "Try values now:", badObj); System.exit(0); } }Figure 9 shows the error dialog that will be displayed.
Figure 9: Error dialog
You can easily test out the justnuff library by selectively commenting out different sections in the JustnuffTest.java test program and running it.
Conclusion
The justnuff library presented in this article can provide a simple-to-use GUI alternative for Java application developers who do not wish to create, maintain, and support complex and sophisticated GUI-based applications. The simplicity and extensibility of the library enable it to be adapted to a wide variety of application scenarios.Sing Li is a consultant, trainer and freelance writer specialising in Java, web applications, distributed computing and peer-to-peer technologies. His recent publications include Early Adopter JXTA, Professional JINI and Professional Apache Tomcat, all published by Wrox Press (RIP).
Comments