0

Using two TextField : texMin and texMax , i want to limit min and max values of my doubleSlider control (cf the custom ui control DoubleSlider here : http://code.google.com/p/javafx-widgets/source/browse/#svn%2Ftrunk%2FDoubleSlider%2Fsrc%2Fdouble_slider )

The DoubleBinded with StringConverter (double to string) works perfectly, but now i don't know how can i filter to get only correct values entered by user in the two TextField after it ENTER keypress..

I don't know if a setOnKeyReleasedEvent filter on value entered by user can work with a bindBidirectional on properties. I think the bindBidirectional bypass my filter (fire an event each time the text change), but i'm not sure.

    def sliderBlockFactory(slider: DoubleSlider): HBox = {
      val minText = new TextField()
      val maxText = new TextField()
      minText.setText(slider.getValue1().toString)
      maxText.setText(slider.getValue2().toString)

      slider.value1Property().addListener(
        new ChangeListener[Number]() {
          override def changed(ov: ObservableValue[_ <: Number], old_val: Number, new_val: Number) {
            if (new_val.doubleValue() == null) {
              minText.setText("")
            } else {
              minText.setText(new_val.doubleValue().toString())
            }
          }
        })

      slider.value2Property().addListener(
        new ChangeListener[Number]() {
          override def changed(ov: ObservableValue[_ <: Number], old_val: Number, new_val: Number) {
            if (new_val.doubleValue() == null) {
              maxText.setText("")
            } else {
              maxText.setText(new_val.doubleValue().toString())
            }
          }
        })

      minText.setPrefWidth(40)
      maxText.setPrefWidth(40)

      val converter: StringConverter[_ <: Number] = new DoubleStringConverter()

      Bindings.bindBidirectional(minText.textProperty(), slider.value1Property(), converter.asInstanceOf[StringConverter[Number]])
      Bindings.bindBidirectional(maxText.textProperty(), slider.value2Property(), converter.asInstanceOf[StringConverter[Number]])

      val uiSliderBlock = new HBox()
      uiSliderBlock.getChildren.addAll(minText, slider, maxText)
      uiSliderBlock
    }

    def doubleSliderFactory(min: Double, max: Double): DoubleSlider = {
      val slider = new DoubleSlider()

      slider.setMin(min)
      slider.setMax(max)
      slider.setShowTickLabels(true)
      slider.setShowTickMarks(true)
      slider.setMajorTickUnit(50)
      slider.setMinorTickCount(25)
      slider.setSnapToTicks(true)
      slider.setPrefWidth(100)
      slider.setBlockIncrement(10)

      slider
    }

val myDoubleSlider = sliderBlockFactory(doubleSliderFactory(0.0, 100.0))

UPDATE 1 :

Thanks to comments, i modify my code source. I giveup DoubleStringConverter because my use case is too complex. I also translate java source code given in comments url into scala :

class DoubleTextField(var minValue: Double, var maxValue: Double, val initialValue: Double) extends TextField {

  require(minValue < maxValue, "DoubleField min value " + minValue + " greater than max value " + maxValue)
  require(maxValue > minValue, "DoubleField max value " + minValue + " less than min value " + maxValue)
  //require(!((minValue <= initialValue) && (initialValue <= maxValue)), "DoubleField initialValue " + initialValue + " not between " + minValue + " and " + maxValue)

  val value: DoubleProperty = new SimpleDoubleProperty(initialValue)

  //scala parsing
  def parseDouble(s: String) = try {
    Some(s.toDouble)
  } catch {
    case _ ⇒ None
  }

  def getValue() = value.getValue()

  def setValue(newValue: Double) = value.setValue(newValue)

  def valueProperty(): DoubleProperty = value

  setText(initialValue.toString())

  val doubleField: DoubleTextField = this

  // make sure the value property is clamped to the required range
  // and update the field's text to be in sync with the value.
  value.addListener(new ChangeListener[Number]() {
    override def changed(observableValue: ObservableValue[_ <: Number], oldValue: Number, newValue: Number) {
      if (newValue == null) {
        doubleField.setText("")
      } else {
        if (newValue.doubleValue() < doubleField.minValue) {
          value.setValue(doubleField.minValue)
          return
        }

        if (newValue.doubleValue() > doubleField.maxValue) {
          value.setValue(doubleField.maxValue)
          return
        }

        if (newValue.doubleValue() == 0 && (textProperty().get() == null || "".equals(textProperty().get()))) {
          // no action required, text property is already blank, we don't need to set it to 0.
        } else {
          doubleField.setText(newValue.toString())
        }
      }
    }
  })

  // restrict key input to numerals.
  this.addEventFilter(KeyEvent.KEY_TYPED, new EventHandler[KeyEvent]() {
    override def handle(keyEvent: KeyEvent) {
      if (!"0123456789".contains(keyEvent.getCharacter())) {
        keyEvent.consume()
      }
    }
  })

  // i don't want a realtime change on my textfield,
  // so i prefer a validate method like focusedProperty (see below)
  /*this.textProperty().addListener(
    new ChangeListener[String]() {
      override def changed(observableValue: ObservableValue[_ <: String], oldValue: String, newValue: String) {
        if (newValue == null || "".equals(newValue)) {
          value.setValue(0)
          return
        }

        parseDouble(newValue) match {
          case Some(d: Double) ⇒
            if (doubleField.minValue > d || d > doubleField.maxValue) {
              textProperty().setValue(oldValue)
              value.set(oldValue.toDouble)
            } else {
              value.set(d)
            }
          case _ ⇒ oldValue //reset to old value
        }

      }
    })*/

  // Detect a change in focus on the text field.. If we lose the focus we take appropriate action
  // problem with boolean : https://stackoverflow.com/questions/11377350/scala-java-interop-class-type-not-converted
  this.focusedProperty().addListener(
    new ChangeListener[java.lang.Boolean]() { db ⇒
      override def changed(observable: ObservableValue[_ <: java.lang.Boolean], oldValue: java.lang.Boolean, newValue: java.lang.Boolean) {
        if (!newValue) {
          parseDouble(doubleField.textProperty().get()) match {
            case Some(d: Double) ⇒
              if (doubleField.minValue > d || d > doubleField.maxValue) {
                textProperty().setValue(doubleField.minValue.toString)
                value.set(textProperty().get().toDouble)
              } else {
                value.set(d)
              }
            case _ ⇒ println("error") //reset to old value
          }
        }
      }
    })

}

The corrected code source for DoubleSlider update by DoubleTextField :

// inspired by :
// https://gist.github.com/1962045
// https://stackoverflow.com/questions/14138082/java-fx-bidirectional-bindings-of-different-properties
// https://forums.oracle.com/forums/thread.jspa?threadID=2311243

def sliderBlockFactory(slider: DoubleSlider): HBox = {
  val minText = new DoubleTextField(slider.getMin, slider.getMax, slider.getMin)
  val maxText = new DoubleTextField(slider.getMin, slider.getMax, slider.getMax)

  slider.value1Property().addListener(
    new ChangeListener[Number]() {
      override def changed(ov: ObservableValue[_ <: Number], old_val: Number, new_val: Number) {
        if (new_val.doubleValue() == null) {
          minText.setText("")
        } else {
          //set new min value of maxText
          maxText.minValue = new_val.doubleValue()

        }
      }
    })

  slider.value2Property().addListener(
    new ChangeListener[Number]() {
      override def changed(ov: ObservableValue[_ <: Number], old_val: Number, new_val: Number) {
        if (new_val.doubleValue() == null) {
          maxText.setText("")
        } else {
          //set new max value of minText
          minText.maxValue = new_val.doubleValue()

        }
      }
    })

  minText.setPrefWidth(40)
  maxText.setPrefWidth(40)

  /*val converter: StringConverter[_ <: Number] = new DoubleStringConverter()
  Bindings.bindBidirectional(minText.textProperty(), slider.value1Property(), converter.asInstanceOf[StringConverter[Number]])
  Bindings.bindBidirectional(maxText.textProperty(), slider.value2Property(), converter.asInstanceOf[StringConverter[Number]])
  */

  minText.valueProperty().bindBidirectional(slider.value1Property())
  maxText.valueProperty().bindBidirectional(slider.value2Property())

  val uiSliderBlock = new HBox()
  uiSliderBlock.getChildren.addAll(minText, slider, maxText)
  uiSliderBlock
}

def doubleSliderFactory(min: Double, max: Double): DoubleSlider = {
  val slider = new DoubleSlider()

  slider.setMin(min)
  slider.setMax(max)
  slider.setShowTickLabels(true)
  slider.setShowTickMarks(true)
  slider.setMajorTickUnit(50)
  slider.setMinorTickCount(25)
  slider.setSnapToTicks(true)
  slider.setPrefWidth(100)
  slider.setBlockIncrement(10)

  slider
}

  val myDoubleSlider = sliderBlockFactory(doubleSliderFactory(0.0, 100.0))

Result :)

enter image description here

This code works, values for min and max of each slider controls are updated correctly, but as you can see, when focus is left and if value are bad i can't retrieve the old value with focused event. My question is simple, is there any way to get the old values in the textField without using changeListener on textProperty ?

Update 2 :

I found my answer here :

Undo behavior on textField value based on value verification after focus lost

Community
  • 1
  • 1
reyman64
  • 523
  • 4
  • 34
  • 73
  • I created a [sample solution](https://forums.oracle.com/forums/thread.jspa?messageID=10692669) earlier to bind between a Slider and TextField, please take a look at that and see if it helps solve your issue. – jewelsea Feb 04 '13 at 19:24
  • Thanks @jewelsea i'm already based my code on your sample ... :] i post question here to see if there is another way to do that, but it seems we're alone ... I found another path to do that here : https://github.com/fxexperience/code/tree/master/FXExperienceControls/src/com/fxexperience/javafx/scene/control – reyman64 Feb 04 '13 at 20:03
  • @jewelsea i update my source code with a first working answer, but i need your help about retrieving old values for textField (see update 1) – reyman64 Feb 04 '13 at 23:08
  • @reyman64 why you define `var value: DoubleProperty` in `DoubleTextField` instead of `val`? I found that fx properties are so nice as to allow for an immutable reference, a singularity in java! – pagoda_5b Feb 05 '13 at 09:10
  • @pagoda_5b you're right, it's a mistake, i update source code :) – reyman64 Feb 05 '13 at 09:28
  • @jewelsea i found my answer with another post/question, thanks for helping. – reyman64 Feb 05 '13 at 14:18

0 Answers0