Reputation: 21
I am a scala newbie who came from JavaFX 1.3 and this is my first post in stackoverflow
In JavaFX 1.3 I can do something like this
property : bind if (condition) value1 else value2
In Scala, I attempted doing something like this:
property <== function1
def function1() = {
if (condition)
value1
else
value2
}
However, it does not seem to be dynamically working. The expression in the condition of the function evaluates only once when the stage appears. I was kind of expecting the values in that expression are evaluated in realtime.
Specifically, I want to resize something to a certain limit and I am using binding to achieve it. So I want the bound function to keep evaluating the expression and give me the appropriate width of something as I resize other things.
Anyway, I will paste the actual codes below:
var stageWidth = DoubleProperty(0)
var stageHeight = DoubleProperty(0)
stageWidth <== stage.scene.width
stageHeight <== stage.scene.height
var origStageWidth = DoubleProperty(stage.scene.width.toDouble)
val origStageHeight = DoubleProperty(stage.scene.height.toDouble)
val origTextClipperWidth = DoubleProperty(textClipper.width.toDouble)
val origTextClipperHeight = DoubleProperty(textClipper.height.toDouble)
val minWidth = DoubleProperty(100)
val origButtonWidth = DoubleProperty(button.prefWidth.toDouble)
textClipper.width <== resize
def resize() ={
var boolResult = (stageWidth - origStageWidth) + origTextClipperWidth > minWidth
if (boolResult.value) {
(stageWidth - origStageWidth) + origTextClipperWidth
} else {
minWidth
}
}
textClipper.height <== (stageHeight - origStageHeight) + origTextClipperHeight
Thanks in advance for your help.
Upvotes: 2
Views: 1604
Reputation: 1533
JavaFX2+ and ScalaFX allow you to dynamically switch two bound properties based on a BooleanProperty
or BooleanBinding
. This can be achieved using when
construct. Your coode
property : bind if (condition) value1 else value2
can be expressed in ScalaFX as:
property <== when (condition) choose value1 otherwise value2
In your specific example assume that we create a width
property that contains dynamically computed width based on the condition boolResult
val width = DoubleProperty(0)
That width
property is bound to conditional expression using when
construct
width <== when(boolResult) choose ((stageWidth - origStageWidth) + origTextClipperWidth) otherwise minWidth
When boolResult
value is true
the first part (after choose) is assigned to width
when it is false
the part after otherwise
is assigned. Here is a complete example that shows this in action:
import scalafx.Includes._
import scalafx.beans.property.DoubleProperty
object ConditionalBindigDemo extends App {
val stageWidth = DoubleProperty(200)
val origStageWidth = DoubleProperty(100)
val origTextClipperWidth = DoubleProperty(20)
val minWidth = DoubleProperty(50)
val boolResult = (stageWidth - origStageWidth) + origTextClipperWidth > minWidth
val width = DoubleProperty(0)
width <== when(boolResult) choose ((stageWidth - origStageWidth) + origTextClipperWidth) otherwise minWidth
// Simple test
printState()
stageWidth() = 150
printState()
stageWidth() = 100
printState()
stageWidth() = 50
printState()
def printState() {
println("stageWidth: " + stageWidth() + ", origStageWidth: " + origStageWidth() + ", width: " + width())
}
}
When you run it you will get output:
stageWidth: 200.0, origStageWidth: 100.0, resizeWidth: 120.0
stageWidth: 150.0, origStageWidth: 100.0, resizeWidth: 70.0
stageWidth: 100.0, origStageWidth: 100.0, resizeWidth: 50.0
stageWidth: 50.0, origStageWidth: 100.0, resizeWidth: 50.0
Upvotes: 1
Reputation: 7373
A standard function/method is not an scalafx.beans.Observable
so it doesn't have the needed "hooks" to invalidate its binding.
A while ago I made some methods to simplify binding creation, just for this purpose.
The following code is used to make functions binding to a string value
import scalafx.Includes._
import scalafx.beans.binding.StringBinding
import scalafx.beans.Observable
import scalafx.collections._
import javafx.collections.ObservableList
import javafx.beans.{ binding => jfxbb }
import jfxbb.ListBinding
def createStringBinding(dependency: Observable*)(computeFunction: => String): StringBinding =
new jfxbb.StringBinding {
//invalidated when the passed dependency becomes invalid
dependency.foreach(this.bind(_))
//use the function to compute the new value
override def computeValue: String = computeFunction
}
In your case you should make a Double binding
//THIS CODE IS NOT TESTED, MAYBE IT NEEDS A LITTLE TWEAKING
def createDoubleBinding(dependency: Observable*)(computeFunction: => Double): DoubleBinding =
new jfxbb.DoubleBinding {
//invalidated when the passed dependency becomes invalid
dependency.foreach(this.bind(_))
//use the function to compute the new value
override def computeValue: Double = computeFunction
}
//and use it like
val resize = createDoubleBinding(
stageWidth,
stageHeight,
origStageWidth,
origStageHeight,
minWidth,
origButtonWidth) {
var boolResult = (stageWidth - origStageWidth) + origTextClipperWidth > minWidth
if (boolResult.value) {
(stageWidth - origStageWidth) + origTextClipperWidth
} else {
minWidth
}
}
textClipper.width <== resize
I suppose it's possible to generalize the createXXXBinding with type parameters adapting to available classes of the javafx.beans.binding
package, but I'm not sure it would be an easy task, since the class hierarchy doesn't help...
Upvotes: 1