Background:
According to the accepted answer of this question, a TextArea
has an underlying Text
node that contains the text and acts as its bounding box. It is useful for calculating the visual (pre-transform) height of the text, even when the text wraps around.
The Text
node is not exposed in the public JavaFX 8 API, but it can be gotten via .lookup()
. Moreover, it seems it is not initialized till after the Scene
is rendered. The above said answer shows how to get it successfully by listening to the Scene
.
Problem:
I have taken the logic of the listener, which works, and implemented it as a binding instead. But the binding does not work, and I cannot understand why. The value of the property updated by the listener is successfully set to the new Text
node, while the value of the binding is always null because it is not detecting the change of Scene
from null to a Scene
object.
public ObjectExpression<Text> getObservableTextNode(TextArea textArea) {
// The underlying text node can be looked up only after applying CSS when there is a scene.
// For that reason we return it as an observable.
// Way #1: Listener
ObjectProperty<Text> property = new SimpleObjectProperty<>();
textArea.sceneProperty().addListener((observableNewScene, oldScene, newScene) -> {
if (newScene != null) {
textArea.applyCss();
Node text = textArea.lookup(".text");
property.set((Text) text);
} else {
property.set(null);
}
});
return property;
// Way #2: Binding - does not update
ObjectBinding<Text> ob = Bindings.createObjectBinding(() -> {
if (textArea.sceneProperty().get() != null) {
textArea.applyCss();
return (Text) textArea.lookup(".text");
}
return null;
}, textArea.sceneProperty());
return ob;
}
As far as I can tell, the logics of the listener and binding are the same. Why then is there a difference?