User Tools

Site Tools


Sidebar

Dave Orme muses about agile and functional programming.

My current work emphasizes SOA applications using Scala, Kubernetes, and AWS with a React-based SPA front-end. I'm also interested in progressive web applications and developer tools.


Blog

Scala, Clojure, and FP

Agile

The Cloud

Data-First Development

Older work

Coconut Palm Software home


Donate Bitcoin:

1Ecnr9vtkC8b9FvmQjQaJ9ZsHB127UzVD6

Keywords:

Kubernetes, Docker, Streaming Data, Spark, Scala, Clojure, OSGi, Karaf, GCP, AWS, SQL

Disclaimer:

Everything I say here is my own opinion and not necessarily that of my employer.

blog:creating_a_swt_custom_control_in_scala

Creating a SWT custom control in Scala

I recently created a simple custom SWT control entirely in Scala, using some XScalaWT to simplify the UI code, to see how much simpler I could make the code using Scala.

Creating a SWT custom control in Java requires quite a bit of boilerplate code. Fortunately, while Scala cannot eliminate all this boilerplate, we will see how it can simplify it some. Of course, suggestions for how to improve it are welcomed in the comments. 8-)

The control

The SWT control we're creating displays a star that can be turned on and off by clicking it.

It follows the following standard SWT conventions:

  1. The standard SWT (parent, style) constructor
  2. Reuses SWT property and event names: selected and selectionListener

The code

Let's step through the code:

Notice that our default constructor only needs the standard SWT parameters and to call super. So we simply define it as a part of the class parameters.

Then we have constants, instance variables, and the internal layout of the control, which we will define using XScalaWT. In this case, our XScalaWT is really simple since what we are doing is really simple. More complicated custom controls would benefit even more from it.

class StarButton(parent : Composite, style : Int) extends Composite(parent, style) {
  val starOnImage = STAR_ON.image;
  val starOffImage = STAR_OFF.image;
 
  var starButton : Label = null;
 
  this.contains(
    label(starButton=_, starOffImage, 
          {e : MouseEvent => setSelected(!selected)})
  )

Here we instantiate a few images. STAR_ON and STAR_OFF are constants declared elsewhere that wrap a JFace ImageDescriptor.

The starButton is a SWT Label that will contain either starOnImage or starOffImage, depending on if the control is selected or not.

The XScalaWT block instantiates the Label and puts the default starOffImage in the label. It also adds a mouseDown listener that toggles the “selected” property.

The remainder of the code manages this property, fires the selection change event, and handles control resizing. First, the property:

  var selected = false;
 
  def getSelected() = selected
 
  def setSelected(newValue : Boolean) = { 
    selected = newValue
    if (selected) starButton.setImage(starOnImage) 
      else starButton.setImage(starOffImage)
    fireSelectionEvent
  }

Here's a standard JavaBean property implemented in Scala. Since SWT doesn't implement bound properties, we don't that here either. We also don't use the @BeanProperty annotation since we need custom code in the #setSelected method.

Next, selection listener handling:

  private var selectionListeners : scala.List[SelectionListener] = Nil
 
  def addSelectionListener(l : SelectionListener) = 
        selectionListeners = l :: selectionListeners
  def removeSelectionListener(l : SelectionListener) = 
        selectionListeners.remove(_==l)
 
  private def fireSelectionEvent = {
    val detail = new Event()
    detail.widget = this
    detail.data = selected.asInstanceOf[Boolean].asInstanceOf[Object]
    val event = new SelectionEvent(detail)
    selectionListeners.foreach(_.widgetSelected(event))
  }

The selection listener handling is pretty standard except that we use a few Scala-isms to simplify the code a bit.

First, we use a scala.List instead of a java.util.List implementation.

The add and remove selectionListener code uses Scala's list append (cons) operator to add the selection listener to the head of the list. Notice that Scala's List#remove accepts a filter function instead of the element to remove. This is only slightly more verbose than Java, but also a lot more powerful. In this context, the underscore represents the parameter being passed to the function (the candidate being evaluated.

Finally, we have the code for managing the control's size:

  addControlListener(new ControlAdapter() {
    override def controlResized(e : ControlEvent) = {
      val newSize = getSize()
      starButton.setBounds(0, 0, newSize.x, newSize.y)
    }
  });
 
  override def computeSize(xDefault : Int, yDefault : Int) = 
    starButton.computeSize(xDefault, yDefault)
}

Aside from Scala's ability to skip the curly braces when the body of a function is a single line, this code is nearly exactly the same as the equivalent Java.

Conclusion

Scala is useful for simplifying SWT custom control creation. For a simple control like this one, we don't see the benefit nearly as much as for a complicated custom control that could benefit greatly from XScalaWT. However, I still find the resulting code clearer, with less line noise than the equiavalent Java.

Can anyone see a way to simplify this further? If so, please leave a comment.

For reference, Here's the whole class:

class StarButton(parent : Composite, style : Int) extends Composite(parent, style) {
  val starOnImage = STAR_ON.image;
  val starOffImage = STAR_OFF.image;
 
  var starButton : Label = null;
  this.contains(
    label(starButton=_, starOffImage, 
          {e : MouseEvent => setSelected(!selected)})
  )
 
  var selected = false;
 
  def getSelected() = selected
 
  def setSelected(newValue : Boolean) = { 
    selected = newValue
    if (selected) starButton.setImage(starOnImage) 
      else starButton.setImage(starOffImage)
    fireSelectionEvent
  }
 
  private var selectionListeners : scala.List[SelectionListener] = Nil
 
  def addSelectionListener(l : SelectionListener) = 
        selectionListeners = l :: selectionListeners
  def removeSelectionListener(l : SelectionListener) = 
        selectionListeners.remove(_==l)
 
  private def fireSelectionEvent = {
    val detail = new Event()
    detail.widget = this
    detail.data = selected.asInstanceOf[Boolean].asInstanceOf[Object]
    val event = new SelectionEvent(detail)
    selectionListeners.foreach(_.widgetSelected(event))
  }
 
  addControlListener(new ControlAdapter() {
    override def controlResized(e : ControlEvent) = {
      val newSize = getSize()
      starButton.setBounds(0, 0, newSize.x, newSize.y)
    }
  });
 
  override def computeSize(xDefault : Int, yDefault : Int) = 
    starButton.computeSize(xDefault, yDefault)
}

~~LINKBACK~~ ~~DISCUSSION:closed~~

blog/creating_a_swt_custom_control_in_scala.txt · Last modified: 2014/10/17 22:08 (external edit)