In previous posts I've demonstrated binding to an attribute, and binding to a sequence. Now I'd like to show you how to bind to a function. This is a very powerful feature because whenever the value of the function changes, the bound variable is updated. Here's a screenshot of today's example program, in which the user selects the diameter of a circle from a slider widget. The program calculates and displays the area of the circle, as well as drawing a circle with the selected diameter in pixels.
Here's the JavaFX Script code for this example:
/*
* BindToFunctionExample.fx - A compiled JavaFX program that demonstrates
* binding to a function.
*
* Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
* to serve as a JavaFX Script example.
*/
import javafx.ui.*;
import javafx.ui.canvas.*;
import java.lang.Math;
import java.lang.System;
class CircleModel {
attribute diameter:Number;
function getArea():Number {
Math.PI * Math.pow(diameter / 2, 2);
}
}
Frame {
var cModel = CircleModel {}
width: 480
height: 560
title: "Bind to Function Example"
background: Color.WHITE
content:
BorderPanel {
center:
Canvas {
content: [
Circle {
cx: 240
cy: 250
radius: bind cModel.diameter * 2
stroke: Color.PURPLE
strokeWidth: 1
fill: Color.CYAN
},
Text {
font:
Font {
face: FontFace.SANSSERIF
style: FontStyle.BOLD
size: 24
}
x: 20
y: 10
stroke: Color.RED
fill: Color.RED
content: bind "Diameter: {cModel.diameter}"
},
Text {
font:
Font {
face: FontFace.SANSSERIF
style: FontStyle.BOLD
size: 24
}
x: 240
y: 10
stroke: Color.RED
fill: Color.RED
content: bind "Area: {%3.2f cModel.getArea()}"
}
]
}
bottom:
Slider {
min: 0
max: 100
border:
TitledBorder {
title: "Diameter:"
}
value: bind cModel.diameter with inverse
minorTickSpacing: 5
majorTickSpacing: 10
paintTicks: true
paintLabels: true
labels: [
SliderLabel {
value: 0
label:
SimpleLabel {
text: "0"
}
},
SliderLabel {
value: 50
label:
SimpleLabel {
text: "50"
}
},
SliderLabel {
value: 100
label:
SimpleLabel {
text: "100"
}
}
]
}
}
visible: true
onClose:
function():Void {
System.exit(0);
}
}
Binding to a Function
As shown above, the slider is bi-directionally bound to the diameter attribute of the CircleModel instance. This attribute affects the value of the getArea() function, so as the value of the diameter attribute changes, the content attribute bound to the getArea() function will reflect its value.
Formatting Output
Another thing that you'll notice in this example is that the value of the getArea() function is formatted to two decimal places when displayed in the Text graphical node. This is due to the formatting capabilities of JavaFX Script that I began discussing in the Happy New Year post.
JavaFX Script Boot Camp Registration Now Open!
I will be offering a 2.5 day JavaFX Script day "boot camp" on Wednesday, April 9 through Friday, April 11, 2008 (ending at noon) in Indianapolis, Indiana. This course is designed to get you quickly up to speed in JavaFX Script application development. A primary reference for this course is my JavaFX Script book, but the course has its own syllabus which includes material covered in the book as well as up to the minute developments in compiled JavaFX Script. Registration is now open, and I am accepting 12 students in this pilot class. The cost of this pilot class will be 900 USD, and additional people from the same company will be 600 USD. You'll need to bring your laptop computer with the latest versions of the JavaFX Script downloads (which I'll specify in more detail as the class date approaches). I'm looking forward to teaching this class and hope that you can attend!
Regards,
Jim Weaver
JavaFX Script: Dynamic Java Scripting for Rich Internet/Client-side Applications
Immediate eBook (PDF) download available at the book's Apress site
A common use case with inverse binding is data entry apps, however, the binding syntax does not provide basic data validation e.g. "July 35, 2008". Why not provide a ValueConverter-style syntactic sugar?
Posted by: Chui Tey | August 10, 2008 at 09:43 PM
t_e_r, EP, Cay, et al,
I received a response to questions about details concerning binding to a function from Per Bothner of the JavaFX Script Compiler team. It is in the mailing list message at the following URL, and for your convenience I've included it in this comment below the URL.
https://openjfx-compiler.dev.java.net/servlets/ReadMsg?list=dev&msgNo=2426
--------------------- Per's Response ---------------------
[While we haven't worked out everything about bind, this
is my understanding of where we are want to be wrt function calls,
and I think there is more-or-less consensus on the following, with a
caveat about some details, such as syntax.]
[Note this does *not* describe the current implementation!]
We distinguish in the data model between "values" and "locations".
A location may "contain" a value, or contain a recipe for
computing a value.
We have two kinds of functions, which are syntactically
distinguished (though we haven't settled on the syntax):
+ A "non-bound function" aka "value function" aka "F3-operation"
takes some number of values, and returns a value.
+ A "bound function" aka "location function" aka "F3-function"
takes some number of locations, and returns a location.
Roughly (we might tweak the details), the body of a bound
function acts like the expression in a bind, and has the
same limitations (if any), to be determined.
(Hybrids are possible, where some but not all of the parameter are
locations or the result isn't a location. However, they're not an
important use-case at this point.)
The default is a non-bound function. Brian has proposed using
the keyword bound to indicate a bound function:
bound function foo (...) { ... }
This add a new keyword, which might be OK, but an alternative
is to either re-use bind, or make bound be context-dependent.
For example
function foo (...) bind { ...}
This has the advantage that it matches the semantic model.
A function call can be either in bind context (roughly, if the call is
lexically inside a bind) or non-bind context (otherwise). (I say
"roughly", because some sub-expression in a bind might "turn off" bind
context. However, this isn't a factor in the current note.)
A "bind context" is loosely something that expects a location.
In bind context, a value is "boxed" to a constant location (or
rather a location initialized to the value).
Calling a non-bound function in non-bind context is trivial.
Calling a bound function in non-bind context does this:
The parameters are evaluated in non-bind context. For
each parameter, a fresh location is created, and it
is initialized with the value resulting from parameter
evaluation. The locations are passed to the function.
When the function returns, the returned location is
de-referenced.
Calling a bound function in bind context evaluates
the arguments in bind mode, resulting in locations.
Those are passed to the function, and the resulting
location is the result of the call.
Calling a non-bound function in bind context evaluates
the arguments in bind mode, resulting in locations.
A fresh FunctionalLocation is created. The locations
are de-referenced, the function called with the resulting
values, and the result becomes the current value of the
FunctionalLocation. Furthermore, (weak) triggers are added
to the parameter locations so when any one of them changes,
the function is re-invoked, and the value updated.
Arithmetic operations, like +, are considered syntactic
sugar for (overloaded) non-bound functions.
An anonymous function can also be bound or non-bound.
Question: is "bindingness" part of the type of a function, or just a
property of the function "value"? I think the latter, since
bindingness doesn't really restrict the kind of functions: When the
compiler generates a call to an unknown function, it doesn't really
need to know whether the function is bound or non-bound, since in both
cases the arguments are evaluated in bind context, and locations
created. A function object (that implements the Function) the
interface just needs two methods: invoke and invokeBound. We can, I
think have two abstract sub-classes: BoundFunction and
NonBoundFunction: The compiler generates the invoke methods for a
non-bound function, and the invokeBound method for a bound function.
The BoundFunction abstract class defines invoke in terms of
invokeBound, and vice versa.
--
--Per Bothner
[email protected] [email protected] http://per.bothner.com/
------------------------------------------------------------
Hope this helps, and thank you Per Bothner!
Jim Weaver
Posted by: Jim Weaver | February 21, 2008 at 06:14 PM
Cay, the current version of the NetBeans plugin is for interpreted JavaFX Script, which serves as a prototype for compiled JavaFX Script (compiles to JVM bytecode). To compile and run this example, see my Obtaining the Compiler post http://learnjavafx.typepad.com/weblog/2007/11/compiler-cheat.html
To all who posted question about specific binding behavior: Binding behavior for compiled JavaFX Script is just now becoming well defined. My understanding of binding to a function is probably too simplistic, which is that if any values in a function change in such a way that causes the function to have a different value, then the bound function is re-evaluated. I posted a request for a succinct description of the behavior of bound functions to the [email protected] mailing list (you can subscribe at the following link: https://openjfx-compiler.dev.java.net/servlets/ProjectMailingListList). When I get an answer I'll post it on this list.
Thanks to all for your great questions.
Jim Weaver
Posted by: Jim Weaver | February 21, 2008 at 01:30 AM
Hi Jim,
I am trying to understand the elusive "bind" operator. I tried compiling your code in the Netbeans 6.0.1 plugin, but that's not been working out. Is the plugin outdated? It is dated 1/24/08.
At any rate, how does "bind" work under the hood? I assume that somehow it collects all possible inputs to the expression that is being bound, and that there are listeners that fire if any of them changes. If any of them does fire, presumably the expression gets re-evaluated, right?
I'd be very grateful for a very specific description of this process. Something that goes beyond "oh, it works just like a spreadsheet".
Thanks,
Cay
Posted by: Cay Horstmann | February 21, 2008 at 12:46 AM