Sunday 22 November 2009

Beware! scala.swing.TextField proclaims EditDone when it isn't

Update: Forget about EditDone. See Update below!

scala.swing.TextField is a basic GUI component that can be used for
letting the user input a line of text. When listening to this component, one can react to an EditDone event:

// Inside some GUI component ...
val textField = new TextField(20)
contents += textField
listenTo(textField)

reactions += {case EditDone(`textField`) =>
println("Ok, searching DB for input "+ textField.text)
}
//...


Fine. Whenever the user (me) hits the Enter key, the message, "Ok,
searching for DB input ...", simulating a database search, is printed.

However, what happens when some unrelated software product suddenly
pops up a window while the user (me) is still inputting text into the
TextField? I tell you what: The evil, non-sentient contraption prints
the simulated search message --- just as if I had hit Enter.

When the TextField loses focus, it emits an EditDone event. But I'm
not done editing. I've only typed "a". I was about to type
"abecedarian". Now the silly thing will search the database for all
words containing the letter "a". I never told it to do that. This
happened just because some other, unrelated, ill-behaving program
grabbed the focus.

Of course, the focus may also be lost because the user voluntarily
changes windows (for instance, in order to Google for "abecedarian").

As far as I can tell, there is no sane way to tell an EditDone event
produced by the user (me) hitting Enter from an EditDone event
produced because the TextField component lost focus. This cannot be
right.

(A while ago, I asked about this on the Scala-user list. Not one single
answer from one single soul in the entire Universe. It feels lonely.)

(I'm using Scala 2.8.)

Update: Forget about EditDone.

What you should do, is not to listen to the TextField, but to TextField.keys. This way, you'll be able to catch a KeyPressed event, and check if the key pressed was Enter. Simple.

It's a bit tricky to figure out, however, since it's not in the TextField Scala docs (you'll have to find your way to scala.swing.Component). This is how it could look:
import swing._
import event._

//...

// Inside some GUI component ...
val textField = new TextField(20)
contents += textField

listenTo(textField.keys)

import Key._
reactions += {case KeyPressed(`textField`, Enter, _, _) =>
println("Ok, searching DB for input "+ textField.text)
}
//...


Thanks to Ingo Maier for explaining this.

Monday 16 November 2009

Source's getLines in Scala 2.8 now strips line end

In Scala 2.8 (not yet officially released), scala.io.Source has been updated.

When reading lines from a file, you do not longer need to trim the lines, since newlines are removed by default. The code to read lines from a file using Source may now look something like this (where fName is a file name (a string)):

val lines = io.Source.fromPath(fName) getLines()

If you want to specify the input file encoding to be UTF-8, you could try this:
val lines = io.Source.fromPath(fName)("UTF8") getLines()

When you look at the API documentation, you'll find that fromPath takes a Codec as a second implicit parameter. Through some mysterious conversion (or "implicit conversion"), you can call it with a string ("UTF8") instead, as in the example above.

Anyway, no more Source.fromFile(fName).getLines.map(_.stripLineEnd). Someone is improving Scala!