JavaFX is Sun’s latest attempt at making an RIA friendly development technology.  It’s aim is to compete with tools such as Adobe Flash/Flex/Air, and Silverlight.  I am not going to do a comparison of these.  Instead, I will focus on my first JavaFX program, Sticky Note.

Sticky Note (launch it, dowload source) is intended to mimic the Windows 7 feature, Sticky Notes.  It provides a sticky note that the user can open and type of reminders.  These will be saved and restored between application runs.  For simplicity’s sake, this script will only allow one note.

What will I need to develop a JavaFX application?

All you need to do is download the latest Netbeans with JavaFX, and make sure you are running the latest version of Java.

In this example, we will be using a few features of JavaFX:

  • Ability to wrap swing components and use them in a JavaFX Application
  • Simple interface for storing state information on Disk
  • Variable binding with ‘bind’ and ‘on replace’ operators
  • Simple API for creating effects, like gradients and drop shadows.

It don’t mean a thing if it ain’t got that swing.

For this program we will need a multi-line textbox.  Unfortunately, the JavaFX API does not provide this.  So we will need to wrap a swing JEditorPane for use.  You can do this pretty easily,

def textbox: JEditorPane = new JEditorPane("text/plain","");
textbox.setBackground(new java.awt.Color(0,0,0,0));
def sbox = SwingComponent.wrap( textbox );

From here, you can easily use it as a normal JavaFX control.

Gotta save state.

JavaFX provides the Storage class to easily store & retrieve data.  We use the public-init variable source to set the name of the file to use.  This is relative to the currently running script.

var storage = Storage { source: "" }
  var resource: Resource = storage.resource;
  var properties: Properties = new Properties();
  var inputStream: InputStream;
  try {
    inputStream = resource.openInputStream();
    text = properties.get("text");
    noteX = Float.parseFloat(properties.get("screenX"));
    noteY = Float.parseFloat(properties.get("screenY"));
    noteWidth = Float.parseFloat(properties.get("width"));
    noteHeight = Float.parseFloat(properties.get("height"));
  } catch (e: Exception) {
    println("Exception in Model.load():{e}");
  } finally {
    try {
    } catch(e: Exception) {
      println("Exception closing inputstream {e}");

Saving is just the opposite.  We are using the dirtyModel variable to track whether the data has been changed since our last save.  Also, we are unable to bind the JEditorPane text property, so we have to manually do it.  I also have logic to prevent multiple saves since this method is called asynchronously.

// handle model storage
var dirtyModel: Boolean = false;
var saveInProgress: Boolean = false;
function saveModel() {

  // update text
  if( not text.equals(textbox.getText() ) ) text = textbox.getText();

  // only one save at a time, and only when necessary
  if( not dirtyModel or saveInProgress ) return;

  saveInProgress = true;
  var resource : Resource = storage.resource;
  var properties : Properties = new Properties();
  properties.put("screenX", "{noteX}");
  properties.put("screenY", "{noteY}");
  properties.put("text", text);
  properties.put("width", "{noteWidth}");
  properties.put("height", "{noteHeight}");
  var outputStream : OutputStream;
  try {
    outputStream = resource.openOutputStream(true);;
  } catch (ioe:IOException) {
    println("IOException saving data:{ioe}");
  } finally {
    try {
    } catch (ioe: IOException) {
      println("IOException closing output stream:{ioe}");
  saveInProgress = false;
  dirtyModel = false;

One thing to note is the usage of brackets inside the quotes when storing in the Properties object.  Anything within brackets in a String will be evaluated.  I used this method b/c it is a little cleaner than using toString().


The most powerful feature of JavaFX is the ability to bind variables and setup triggers.  In this example, we use triggers to monitor whether the model is dirty or not.

   // model variables
   var noteWidth: Float = minWidth on replace { dirtyModel = true };
   var noteHeight: Float = minHeight on replace { dirtyModel = true };
   var noteX: Float = 100 on replace { dirtyModel = true };
   var noteY: Float = 100 on replace { dirtyModel = true };
   var text = "Click here to write notes" on replace { dirtyModel = true };

You could access the old value if you would like by using the following form:

var noteWidth: Float = minWidth on replace oldValue { dirtyModel = true; }

I have also used binding so that components are resized according to the noteWidth variable.  That way all we have to do is set the noteWidth variable and everything will adjust itself accordingly. 

The following example is the code for a rectangle that sits in the lower right corner and handles the resizing of the note.  It demonstrates binding as well as updating of the global variable ‘noteWidth’.

  // resize box
  Rectangle {
    width: 12
    height: 12
    x: bind noteWidth - 12 - dropShadowRadius
    y: bind noteHeight - 12 - dropShadowRadius
    fill: Color.TRANSPARENT
    cursor: Cursor.SE_RESIZE
    onMouseDragged: function(e : MouseEvent) {
      if( e.sceneX < minWidth ) { noteWidth = minWidth; }
      else noteWidth = e.sceneX;
      if( e.sceneY < minHeight ) { noteHeight = minHeight; }
      else noteHeight = e.sceneY;

Note: I have read that bind operators are less performant than using on replace triggers for similar functionality.  I plan to investigate this in the near future.

Graphics & Effects using JavaFX

Effects are easy to achieve in JavaFX.  In this program I used a gradient for the background as well as a drop shadow.

JavaFX Background Gradient using LinearGradient:

   LinearGradient {
     startX: 0.0, startY: 0.0, endX: 0.0, endY: 1.0
     stops: [ Stop { offset: 0.0 color: noteColorTop },
                Stop { offset: 1.0 color: noteColorBottom } ]

Positional variables must be between 0 and 1 unless proportional is set to false.  In which case, the values are absolute.

JavaFX Drop Shadow using DropShadow:

   DropShadow { 
     radius: dropShadowRadius 
     offsetY: 3 
     color: Color.color(0.4, 0.4, 0.4) 

I am aware of the dragging issues experienced when dragging up and down.  However, I am unable to resolve them (not having much experience in desktop app development).  Let me know if you figure out a way to fix it.


You can download the source code here, or run Sticky Note.


More JavaFX Resources:


UPDATE (1/16/2010):

This project is now hosted at github at

Tags: ,