Peter Perez
Peter Perez

Reputation: 63

How can I filter input to a Scala swing TextField

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

Answers (2)

Ursus Dei
Ursus Dei

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

aemxdp
aemxdp

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

Related Questions