Groovy SwingBuilder

I’ve been playing with Groovy’s SwingBuilder lately. It allows you to build Java Swing interfaces very succinctly and in a natural hierarchical way. For example, here’s how you would make a sample frame with a few controls in it:

import groovy.swing.SwingBuilder;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

def sb = new SwingBuilder()
sb.frame(title: 'Sample SwingBuilder app',
         location: [400, 50],
         pack: true,
         show: true,
         defaultCloseOperation: JFrame.EXIT_ON_CLOSE) {
  gridLayout(columns: 2, rows: 4)
  label('First name:')
  textField(id: 'firstName')       

  label('Last name:')
  textField(id: 'lastName')

  label('Age:')
  textField(id: 'age')

  button(text: 'Show', actionPerformed: {
    sb.optionPane().showMessageDialog(null, 
      "Hello ${firstName.text} ${lastName.text}. You are ${age.text} years old.", 
      "Data you entered",
      JOptionPane.INFORMATION_MESSAGE)
  })
}

Save the above in SwingTest.groovy and run it with groovy SwingTest.groovy. You will get something similar to this:

Compare this to the code. In the code, we first set the layout to be grid layout with 2 columns and 4 rows. Then we have three label-text field rows (label goes into the first column and text field into the second column). We put the button in the first column of the last row.

It’s very simple and clean, no additional cruft. Some highlights:

  • Label text is the default property, so label('Hello') will create the label with it’s text set to Hello. This applies to other components, too.
  • Properties are auto-wired, so doing textField(id: 'abc') will make a text field and set it’s ID property.
  • You will note ID is not a default property of JTextField. This is the addition by SwingBuilder, allowing it’s use later. This is how firstName.text works inside the message string.
  • You will notice the names of the components correspond to Java class names most of the time. To get the name, drop J and camel-case it: ** JTextField -> textField ** JButton -> button ** JOptionPane -> optionPane ** etc.

When you press show, a standard JOptionPane message dialog will appear:

showing the usage of component IDs and another SwingBuilder-wired component, optionPane.

SwingBuilder is Groovy

The most important thing is that all the code in the above example is pure Groovy. That is, you can use all normal Groovy constructs to do what you need. For example, you can do the following:

import groovy.swing.SwingBuilder;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

def sb = new SwingBuilder()
sb.frame(title: 'Sample SwingBuilder app',
         location: [400, 50],
         pack: true,
         show: true,
         defaultCloseOperation: JFrame.EXIT_ON_CLOSE) {
  def noItems = 5

  gridLayout(columns: 2, rows: noItems + 1)

  (1..noItems).each {
    label("Item $it:")
    textField(id: "item$it")       
  }

  button(text: 'Show', actionPerformed: {
    def items = (1..noItems).collect {
      "Item $it: " + sb."item$it".text
    }.join("\n")
    sb.optionPane().showMessageDialog(null, "Your items:\n$items",
     "Data you entered", JOptionPane.INFORMATION_MESSAGE)
  })
}

Save the above as SwingTest2.groovy and run it with groovy SwingTest2.groovy. You will get something like the following:

If you take a look at the above code, you will spot that regular groovy constructs are used:

  • def noItems = 5 defines the number of items we want shown
  • The following:
(1..noItems).each { 
  ... 
} 

is a regular Groovy Range + each method call

  • The following:
def items = (1..noItems).collect {
  "Item $it: " + sb."item$it".text
}.join("\n")

is again a Range + collect method call, also using the regular Groovy variable references (referencing sb builder and items in it with item$it).

If you enter the data as the above, clicking on Show button will yield:

All in all, I don’t think you could get much closer to a clean programmatic interface then this.