Monday, 26 April 2010

Scala Swing ComboBox

Swing has got a bad reputation. If it's not people saying it's too slow, it's people saying it's dead.

I've never had a problem with Swing. I've always been able to make it do what I want, and always managed to make it look good in the process.

I've been working completely in Scala now for at least the last year and recently I had to knock up a GUI. I could have done this using Swing from Scala as Scala is nice and flexible like that. However, I got to like the "Scala way of doing things" so much, I thought I would have a look at the scala.swing library.

I'm glad I did. It makes Swing a joy to use as I'll show you over the next couple of posts.

One of the problems people have with Swing is that it doesn't support generics in components such as JComboBox and JList. Scala Swing does allow this. In fact, they pick up the type automatically.

import swing.Swing._
import swing._
import event.{ButtonClicked, WindowClosing}
import swing.ListView._

class ScalaSwingDemo extends Frame {
title = "Scala Swing Demo"

case class OS(name:String, version:String)
val comboBox = new ComboBox(List(OS("Ubuntu", "10.04"), OS("Snow Leopard", "10.6.3"), OS("Windows", "7"))) {
renderer = Renderer(_.name) // The renderer is just the name field of the OS class.
}

val button = new Button {
text = "Output Selected OS"
}

contents = new FlowPanel {
contents += new Label("Select OS:")
contents += comboBox
contents += button
}

listenTo(button) // Means this frame will listen to this button.

reactions += {
case WindowClosing(e) => System.exit(0)
case ButtonClicked(`button`) => { // This is what happens when the button is clicked.
val selectedOS = comboBox.selection.item // This returns the type OS.
println("Selected OS Name: " + selectedOS.name + " Version: " + selectedOS.version)
}
}

pack
centerOnScreen
visible = true
}

object ScalaSwingDemoRunner {
def main(args: Array[String]) {
onEDT(new ScalaSwingDemo)
}
}
The main method is specified in the object ScalaSwingDemoRunner. I call onEDT which is a method on the scala.swing.Scala object. This method takes a function. In this case all I want to do is create my main frame.

The whole body of the ScalaSwingDemo is the constructor in Scala. I extend the scala Frame class. This allows me to set the title with an equals sign. Note that this is actually calling a method and not setting a field.

The next thing I do is define a class called OS. In Scala, case classes provide a nice way to specify a class without all the boilerplate associated with Java. I then create a ComboBox and pass through a List of these OS classes. It's nice to be able to specify a List with a "literal". I think this is coming in JDK7 as well.

Inside the ComboBox definition I specify the renderer. In this case I use a standard Renderer object that takes a function. I know that the OS class has got the "getter method" called name. I want the result of the name method call to be displayed in my ComboBox, so I specify that there. I say "whatever gets passed into this renderer, call the name method and display the result". This is all statically typed. The ComboBox knows that it is displaying OS objects so the renderer knows that it will get something with a name method on it.

I then create a Button and specify the text of the Button in a similar way to the way I specified the title of the Frame.

Next I specify the contents of the Frame. All I'm using here is a FlowPanel, which is very similar to a JPanel with a FlowLayout set.

If I want to react to the Button I have got various options but in this case I just say the Frame should listen to it.

I then add reactions to the Frame to specify what to do when certain events I'm interested in happen. In this case it is exiting when the Frame is closing and printing out info about the selection in the ComboBox when the button is clicked.

This is the interesting bit. Where I call, comboBox.selection.item I actually get back an object of type OS. That's pretty sweet and allows me to call methods on it without casting.

Here is a screen shot of it in action:


This is a pretty simple example but I hope it shows quite a few interesting ways in which Scala Swing makes creating a GUI a joy.

Oh, and there are no performance problems!