Saturday, 8 May 2010

Scala Swing ComboBox part 2

In the previous entry here I showed how to create an application using Scala Swing that contained a ComboBox and Button.

In this post I want to expand that example by showing you how to select a ComboBox item programmatically and also listen to changes to the ComboBox.

The code below shows how to change the ComboBox selection programmatically.

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

class ScalaSwingDemo2 extends Frame {
title = "Scala Swing Demo 2"
case class OS(name:String, version:String)
val snowLeopard = OS("Snow Leopard", "10.6.3")
val comboBox = new ComboBox(List(OS("Ubuntu", "10.04"), snowLeopard, OS("Windows", "7"))) {
renderer = Renderer(_.name)
}
val button = new Button {
text = "Select Snow Leopard OS"
}
contents = new FlowPanel {
contents += new Label("Select OS:")
contents += comboBox
contents += button
}
listenTo(button)
reactions += {
case WindowClosing(e) => System.exit(0)
case ButtonClicked(`button`) => comboBox.selection.item = snowLeopard
}
pack
centerOnScreen
visible = true
}
object ScalaSwingDemo2Runner {
def main(args: Array[String]) {
onEDT(new ScalaSwingDemo2)
}
}
This code is very similar to before so I won't bother explaining it again. The difference is what happens when the button is clicked. The code called is:

comboBox.selection.item = snowLeopard

This sets the selected item of the ComboBox to the specified OS class, in this case Snow Leopard. Notice that it is expecting a class of type OS without casting. This is statically checked and I can prove this by changing the line to:

comboBox.selection.item = "Snow Leopard"

If you try and compile this you will get a compile time error.

This is very simple but is just another example of how concise Scala can be.

The next bit of code shows how to listen to selection changes on the ComboBox.

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

class ScalaSwingDemo3 extends Frame {
title = "Scala Swing Demo 3"
case class OS(name:String, version:String)
val snowLeopard = OS("Snow Leopard", "10.6.3")
val comboBox = new ComboBox(List(OS("Ubuntu", "10.04"), snowLeopard, OS("Windows", "7"))) {
renderer = Renderer(_.name)
}
val button = new Button {
text = "Select Snow Leopard OS"
}
contents = new FlowPanel {
contents += new Label("Select OS:")
contents += comboBox
contents += button
}
listenTo(button)
listenTo(comboBox.selection)
reactions += {
case WindowClosing(e) => System.exit(0)
case ButtonClicked(`button`) => comboBox.selection.item = snowLeopard
case SelectionChanged(`comboBox`) => {
val selectedOS = comboBox.selection.item
println("Selected OS Name: " + selectedOS.name + " Version: " + selectedOS.version)
}
}
pack
centerOnScreen
visible = true
}
object ScalaSwingDemo3Runner {
def main(args: Array[String]) {
onEDT(new ScalaSwingDemo3)
}
}
We've added another reaction to our Frame and told our Frame to listen to another publisher. We want to listen to the selection of the ComboBox so achieve this with this line:

listenTo(comboBox.selection)

When a user selects a different item in the ComboBox, the block of code after the case SelectionChanged(`comboBox`) => is run. In this case the block of code just prints out information about the selected item.

The button functionality is still available. If you click the button (when Snow Leopard is not selected), you will see that Snow Leopard is selected, and the SelectionChanged block is run. This is what you would expect. However, when selecting something programmatically, sometimes you would not want that block of code to be run. Scala Swing provides an easy way to deal with this, which the following code demonstrates.

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

class ScalaSwingDemo4 extends Frame {
title = "Scala Swing Demo 4"
case class OS(name:String, version:String)
val snowLeopard = OS("Snow Leopard", "10.6.3")
val comboBox = new ComboBox(List(OS("Ubuntu", "10.04"), snowLeopard, OS("Windows", "7"))) {
renderer = Renderer(_.name)
}
val button = new Button {
text = "Select Snow Leopard OS"
}
contents = new FlowPanel {
contents += new Label("Select OS:")
contents += comboBox
contents += button
}
listenTo(button)
listenTo(comboBox.selection)
reactions += {
case WindowClosing(e) => System.exit(0)
case ButtonClicked(`button`) => {
deafTo(comboBox.selection)
comboBox.selection.item = snowLeopard
listenTo(comboBox.selection)
}
case SelectionChanged(`comboBox`) => {
val selectedOS = comboBox.selection.item
println("Selected OS Name: " + selectedOS.name + " Version: " + selectedOS.version)
}
}
pack
centerOnScreen
visible = true
}
object ScalaSwingDemo4Runner {
def main(args: Array[String]) {
onEDT(new ScalaSwingDemo4)
}
}
Here, when the button is clicked, before we select the snowLeopard item, we tell the frame to be deaf to the ComboBox selection with this line of code:

deafTo(comboBox.selection)

Once the item has been selected we want to listen to the selection again so call:

listenTo(comboBox.selection)

That's it for this post. I hope Java Swing developers recognise what I'm doing here and how much easier it is when using Scala Swing. Remember as well, I'm only using the standard Scala Swing library.

1 comment:

  1. To make the comboBox editable, I changed comboBox declaration this way:

    val comboBox = new ComboBox(List(OS("Ubuntu", "10.04"), snowLeopard, OS("Windows", "7"))) {
    implicit def editor(c: ComboBox[OS]): ComboBox.Editor[OS] =
    new ComboBox.BuiltInEditor(this)(
    s => OS(s,"no version"), // could split s with a possible comma separator
    os => os.name) {}
    makeEditable();
    // renderer = Renderer(_.name)
    renderer = Renderer(_.toString)
    }

    It took me quite some time to figure out the editor defintion.
    But, after entering "Solaris" in the box, it gets transformed thus:

    OS(OS(Solaris,no version),no version)

    ReplyDelete