James Williams
LinkedInMastodonGithub

WWIG - JFlubber

Tags: Groovy

Let me first state that it is very possible that any of the applications in this series, if reimplemented today in the original implementation language, might see a reduction in code size/simplification due to libraries, methods, or techniques that weren't available the first time around. That is probably the case with this first example as I am pretty sure that some of the implementation code eventually made its way into the SwingX components.

jFlubber, in its original version, was created by Dick Wall (of Java Posse fame) as a tool to mark flub spots during the recording of the podcast and as a way to learn Matisse. In its second iteration, Romain[Guy] injected some bling. That's the version I've recreated in Groovy.

Dick's Original Version Romain's version

Creating the buttons

Instead of creating each button with a half a dozen method calls as Matisse did, I was able to refactor it into a single function that takes a string and a closure for the ActionListener.

def createButton(buttonString, action) {
  return swing.button(borderPainted: false, contentAreaFilled: false, focusPainted: false,
    icon: swing.imageIcon(getClass().getResource(baseIconString + buttonString + '-button.png')),
     pressedIcon: swing.imageIcon(getClass().getResource(baseIconString + buttonString + '-button-pressed.png')),
     actionPerformed: action)
}

TimeLabel and WoodenContentPane

Because Groovy doesn't allow inner classes, these had to be broken out of the main form class file. For the time label, thanks to the existence of Painters, I was able to construct a component duplicating the effect using a CompoundPainter :

private TimeLabel() {
  compoundPainter = swing.compoundPainter() {
     imagePainter(image: ImageIO.read(getClass().getResource("images/metal.png")),scaleToFit: true)
     textPainter(id: 'txtPainter', font: new Font("Arial", Font.PLAIN, 36), fillPaint: Color.white) {
             shadowPathEffect()
         }
  }
  setBackgroundPainter(compoundPainter)
}

Only cosmetic changes were made to the WoodenContentPane class file. registerBeanFactory registers the class with the builder so that we can reference it like any other built-in component.

swing = new SwingXBuilder()
swing.lookAndFeel(new org.jdesktop.swingx.plaf.nimbus.NimbusLookAndFeel())
swing.registerBeanFactory("woodenPane", WoodenContentPane)
swing.registerBeanFactory("timeLabel", TimeLabel)
...
timeLabel = swing.timeLabel(text: '0:00')

And now for the pièce de résistance, the code to build and layout the GUI. Warning: ymmv if your resolution is less than 1024x768. I've not yet found a reliable way to duplicate the efficient resizing from GroupLayout in MigLayout. The spacing is a little bit baked in. A sacrifice made for speed's sake.

def pane = swing.woodenPane(layout: new MigLayout()) {
  def x = centerObject(261)
  widget(timeLabel, constraints: "id timeLabel, w 261!, h 81!, x " + x)
  widget(startButton, constraints: 'id startButton,x timeLabel.x-5, y timeLabel.y2+10')
  widget(stopButton, constraints: 'id stopButton, x startButton.x2+45, y startButton.y, x2 timeLabel.x2')
  flubX = centerObject(flubButton.getSize().getWidth())

  widget(flubButton, constraints: 'id flubButton, x 75, y 150,wrap')
  scrollPane(constraints: 'id scrollPane,x timeLabel.x+10, x2 timeLabel.x2, y flubButton.y2+10',
    horizontalScrollBarPolicy: ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER) {
        widget(flubPointsTextArea)
    }
  widget(saveButton, constraints: 'x startButton.x, y scrollPane.y2+20')
  widget(clearButton, constraints: 'x stopButton.x, y scrollPane.y2+20, x2 timeLabel.x2')

}

And here's how the Groovy version looks. Download the tar'd project archive here.