Reputation: 63
Is there a convenient way to add a filter to input to a scala.swing.TextComponent, so that the user can only input integers/floats/whatever? Especially, is there a way to filter pastes into a text field? In Java, I've used a DocumentFilter to do that. I've tried a few variations on this:
object inputField extends TextField{
peer.getDocument.asInstanceOf[AbstractDocument].setDocumentFilter(new DocumentFilter{
def insertString(fb: FilterBypass, offs: Integer, str: String, a: AttributeSet){
if(str.forall((c)=>c.isDigit)) super.insertString(fb, offs, str, a)
}
def replace(fb: FilterBypass, offs: Integer, l: Integer, str: String, a: AttributeSet){
if(str.forall((c)=>c.isDigit)) super.replace(fb, offs, l, str, a)
}
})
}
Which isn't working. Am I doing that wrong, or does Scala ignore document filters? Is there another way to do this? I might be ok with using an entirely java.swing GUI if that's what it takes.
Upvotes: 1
Views: 830
Reputation: 11
This is class that correctly handles all valid numbers except exponents:
class NumberField(initialValue: Double) extends TextField(initialValue.toString) {
// Note: exponents are not allowed
val numberFormatException = new NumberFormatException
listenTo(keys)
reactions += {
case event: KeyTyped => {
try {
// Don't allow "d" or "f" in the string even though it parses, e.g. 3.5d
if (event.char.isLetter)
throw numberFormatException
// Don't allow string that doesn't parse to a double
(text.substring(0, caret.position) +
event.char +
text.substring(caret.position)).toDouble
} catch {
case exception: NumberFormatException => event.consume()
}
}
}
def value : Double = text.toDouble
}
Upvotes: 1
Reputation: 1339
You can achieve this using reactions:
import swing._
import swing.event._
object SwingApp extends SimpleSwingApplication {
def top = new MainFrame {
contents = new TextField {
listenTo(keys)
reactions += { case e: KeyTyped =>
if (!e.char.isDigit) e.consume
}
}
}
}
Update: Oh, I agree, that fits only relatively trivial cases. I've found problem with your original solution: you use Integers where Ints needed, so your methods are just new ones, not implementations of methods of abstract class. Here's how it works for me:
contents = new TextField {
object IntegralFilter extends DocumentFilter {
override def insertString(fb: FilterBypass, offs: Int, str: String, a: AttributeSet){
if(str.forall((c)=>c.isDigit)) super.insertString(fb, offs, str, a)
}
override def replace(fb: FilterBypass, offs: Int, l: Int, str: String, a: AttributeSet){
if(str.forall((c)=>c.isDigit)) super.replace(fb, offs, l, str, a)
}
}
peer.getDocument().asInstanceOf[AbstractDocument].setDocumentFilter(IntegralFilter)
}
Notice the use of "override". That's how I got compiler error and corresponding IDE tip, that if I really want to override something with this, I have to change signature.
Upvotes: 2