The Kaptain on … stuff

05 Apr, 2009

SwingX busy label demo in Griffon and Groovy

Posted by: TheKaptain In: Development

The first demonstration panel in the SwingLabs demo is for the JXBusyLabel, a simple component that does exactly what it says – inform Users of the application that progress is occurring. The setup is highly configurable, allowing changes to the color, shape and size of the rendered label.

The SwingX demonstration makes use of an inner ‘DetailsPane’ class that nicely handles a lot of the gridbag layout details, but I didn’t really want to spend the extra time Groovifying a perfectly good and already implemented helper class, so I just threw the java code in the Griffon src directory and that was that. Being able to seamlessly mix Java and Groovy is a definite plus, let me tell you. Below is an example of adding a new DetailsPane using the builder DSL, utilizing a GridBagConstants variable named ‘gbc’ and assigning the foreground color based on a model property.

genPane = jxpanel(constraints: gbc,
                              new DetailsPane("General settings:", model.FOREGROUND))

Here is a side-by-side comparison of Java and then the equivalent Groovy code for creating and initializing a new JXBusyLabel. At first they don’t look a whole lot different, but if you notice, the Groovy version is actually a one-liner, and uses the bind() syntax to set colors based on a model backed property.

 label = new JXBusyLabel(new Dimension((int) (W / 2), (int) (H / 2)));
 label.getBusyPainter().setHighlightColor( new Color(44, 61, 146).darker());
 label.getBusyPainter() .setBaseColor(new Color(168, 204, 241).brighter());
 label.setOpaque(false);
 label.setHorizontalAlignment(JLabel.CENTER);
label = jxbusyLabel(
       preferredSize: new Dimension(model.W / 2 as int,model.H / 2 as int),
       busyPainter: busyPainter(highlightColor: bind{ model.busyColor },
       baseColor: bind{model.baseColor}), opaque: false, horizontalAlignment: JLabel.CENTER)

SwingX contains a specialized button that wraps a color chooser component, and here we can look at the differences between the Java and Groovy syntax for instantiating one. The JXColorSelectionButton class isn’t actually a node made available by the SwingXBuilder, but adding to the builder syntax dynamically is simple to say the least using a call to ‘registerBeanFactory’. Also in the Groovy version, here we’re not directly updating the color on the label, we’re updating the model. While the background color change seems to be correctly updated through the bound ‘baseColor’ model property, it does not repaint automagically. I’m sure there’s a way to do this, it just hasn’t popped up yet.

JXColorSelectionButton baseColBtn = new JXColorSelectionButton();
baseColBtn.setBackground(label.getBusyPainter().getBaseColor());
baseColBtn.addPropertyChangeListener("background", new PropertyChangeListener() {
      public void propertyChange(PropertyChangeEvent evt) {
           label.getBusyPainter().setBaseColor((Color) evt.getNewValue());
           label.repaint();
      }});
registerBeanFactory('colorButton', JXColorSelectionButton.class)
baseColBtn = colorButton(background: model.baseColor)
baseColBtn.addPropertyChangeListener("background",
      {PropertyChangeEvent evt ->
           model.baseColor = (Color) evt.getNewValue()
           label.repaint()     //need to figure out how to remove this call!!!
      } as PropertyChangeListener)

The Java code uses an anonymous inner class to kick off the process.

new JButton(new AbstractAction("Start") {
      public void actionPerformed(ActionEvent e) {
           label.setBusy(!label.isBusy());
           putValue(Action.NAME, label.isBusy() ? "Stop" : "Start");
      }
 })

No anonymous inner classes in Groovy, and with the Griffon framework we can separate the concerns a bit better anyhow. This code is split up between the view and the controller classes. Note that the controller can reference(and update) the action by acting through its injected view component.

/* action and button defined in the view */
actions {
      action(id: "startAction",
      name: model.START_NAME,
      mnemonic: "S",
      accelerator: shortcut("S"),
      closure: controller.start)
 }
button(startAction)
...
/* controller action */
def start =  { ActionEvent evt ->
      view.label.setBusy(!view.label.isBusy())
      view.startAction.putValue(Action.NAME, view.label.isBusy() ? "Stop" : "Start")
 }

Finally, here’s a code sample of how to add a listener to an existing component and bind the action to a method on the controller:

     xSlider.addChangeListener(controller.&fcl as ChangeListener)

In most cases, the sample code here is not trying very hard to shorten the syntax; there’s a lot of room for improvement and further Groovification. I’m sure there’s a better way to add listeners, for instance. Anyhow, aside from a few details, the application is up and running and doing the same thing as the original. Total effort expended: probably about 10 hours, a good portion of it spend Googling and reading. Next step is to write some tests to get better at that side of the equation. I’d be including more links about the excellent SwingX components in this post, but the SwingLabs website has been reporting “Maximum Connections Reached: 4096 — Retry later” all day. Hope everything gets fixed soon guys!

If you’re interested in seeing this demo live and in action, there’s a link to a short Jing video below.

Click here for video of the JXBusyLabel demo running on Griffon.

Share
Get Adobe Flash player