tornadofx
tornadofx copied to clipboard
binding tool
Hello,
I am almost sure, there is a same or close to the same utility function in tornadofx, but I haven't found. But in the improbable case, it is not yet implemented, I share my function for overcomming the unfortunate parameter order on multi-dependency binding, the library may find it useful.
In the vanila JavaFx, there is utility functions for creating bindings depending on more than one properties. These family of functions are reside in Bindings utility class. Unfortunately, the parameter order prevents moving the lambda outside of parenthesis. Also, there is several functions for each different property types.
Building on the reified parameters these bindings could be cummulatef into a single extension function:
inline fun <reified T : Any> Property<T>.bind(vararg dependencies: Observable,
noinline op: () -> T): Binding<T> {
val binding = createObjectBinding(Callable { op() }, *dependencies)
this.bind(binding)
return binding
}
Or, if one prefers the one-liner version:
inline fun <reified T : Any> Property<T>.bind(vararg dependencies: Observable,
noinline op: () -> T): Binding<T>
= createObjectBinding(Callable { op() }, *dependencies).also { this.bind(it) }
With this function, multi-property binding could be written in a more kotlin-like manner:
val firstroperty : IntegerProperty
var first by firstProperty
val secondProperty : IntegerProperty
var second by secondProperty
val sumProperty : IntegerProperty
var sum by sumProperty
sumProperty.bind( firstProperty, secondProperty ) { first + second }
There are already a whole bunch of functions for creating bindings in various ways in TornadoFX. They "fix" a number of problems including:
- Making argument order more idiomatic for Kotlin
- Have overloads with and without receivers
- Use operator overloading for common binding operations
So this new bind
function won't be particularly useful. For example, your example can already be written as:
sumProperty.bind(firstProperty + secondProperty)
Also, as a side note, nothing in your functions appears to use the reified type as such, so reification is not needed.
Thanks for the help, pointing out the file of the binding functions. As I wrote, I was sure there is already a solution for this.
What I found is the closest to my solution is the
fun <T> ObservableValue<T>.XXXBinding(vararg dependencies: Observable, op: (T?) -> XXX): XXXBinding
= Bindings.createXXXBinding(Callable { op(value) }, this, *dependencies)
family of functions. These are solving the main problem I was faced with: moving the dependents in front of the lambda. This reduce the novelty and usefulness of my version.
One theoritical question: has the typed binding (having a bunch of identical functions for each type of result) any advantage over my single, general one?
My function also does one more thing: assigns the created binding to the target property, because, when one has to use these complex, multi dependent bindings, it is almost always assigned to a single property. It is arguable whether to combine the creation of the binding and its assignment to the target property is better or not, but I always feel there should be a convenient method when two operation is almost always follows each other.
But I guess this is kind of taste of preference. I like the form of
textProperty().bind(isRunningProperty, targetTickProperty) { if (isRunning) "Running till $targetTick" else "Paused" }
On the other hand, you were right about the reified parameter as well. It remained after my first attempt, when I thought I would have to create different XXXBinding (IntBinding, FloatBinding, etc.) based on the current type argument. However, as ObjectBinding is turned out to be suitable for all cases, the reification is not needed, indeed.
As for your simplification, that was only a simplified example I gave. Where I use this binding, the body of the binding consist if
s and other transformations of several, different type of properties. That can't be expressed with the operators.