« January 2008 | Main | March 2008 »

February 2008

February 27, 2008

Part Three of Creating a Tetris Program in Compiled JavaFX Script

Doh!  I accidentally hit the Save button with the Publish option selected, before this post was ready (again!!!).  Article coming soon, and sorry for the inconvenience.  Meanwhile, since it is the third part of a series on building a Tetris game in compiled JavaFX Script, you may want to check out the first two parts:

1) Let's Create a Tetris Game in Compiled JavaFX Script

2) Creating a Tetris Program (Part Two) in Compiled JavaFX Script

By the way, I'll be speaking on JavaFX Script at Sun Tech Days in Sydney, Australia, on March 4 - 6, 2008.  If you're in the area, please check it out and say hi!

Suntechdaysaus

Regards,

Jim Weaver


February 24, 2008

Creating a Tetris Program (Part Two) in Compiled JavaFX Script

This is the second part of a mini-series in which we're creating a Tetris game in compiled JavaFX Script together.  If you haven't already, please take a look at the Let's Create a Tetris Game in Compiled JavaFX Script post to get up to speed.

Here's a screen shot of the executing program in its current state:

Tetris_2

As you can see, I've made a few changes to the UI, making it look a little more like a Tetris game.  I also refactored some of the code, introduced a new class to represent the playing field, moved the TetrisShapeType class to the tetris_model package, and added some functionality.

Compile and run the code that you see below, and click the Play button when the screen shown above appears.  This causes one of the seven tetromino types to begin falling down the playing field.  Clicking the Rotate button rotates the tetromino clockwise, clicking the Left button moves it to left, and clicking the Right button moves it to the right.  When the tetromino arrives at the bottom, it disappears and a new randomly chosen tetromino shape begins falling.  To exit the program, close the window.

In its current state, the program doesn't have all of the functionality required in a Tetris game (for example, the tetrominoes don't stack up).  We'll get there, and hopefully we'll all learn more about developing JavaFX Script applications in the process.  Here's the current source code:

The main program, named TetrisMain.fx, that declaratively expresses the UI and starts things up:

/*
*  TetrisMain.fx - The main program for a compiled JavaFX Script Tetris game
*
*  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
*  to serve as a compiled JavaFX Script example.
*/
package tetris_ui;

import javafx.ui.*;
import javafx.ui.canvas.*;
import java.lang.System;
import tetris_model.*;

Frame {
  var model =
    TetrisModel {
      
    }
  var canvas:Canvas
  width: 480
  height: 500
  title: "TetrisJFX"
  background: Color.LIGHTGREY
  content:
    BorderPanel {
      center:
        FlowPanel {
          content: [
            Canvas {
              content:
                TetrisPlayingField {
                  model: model
                }
            }
          ]
        }
      bottom:
        FlowPanel {
          content: [
            Button {
              text: "Play"
              action:
                function() {
                  model.t.start();
                }
            },
            Button {
              text: "Rotate"
              action:
                function() {
                  model.rotate90();
                }
            },
            Button {
              text: "Left"
              action:
                function() {
                  model.moveLeft();
                }
            },
            Button {
              text: "Right"
              action:
                function() {
                  model.moveRight();
                }
            }
          ]
        }
    }
  visible: true
  onClose:
    function():Void {
      System.exit(0);
    }
}

The TetrisShape custom graphical component.  By the way, I refactored the big ugly if/else statement from this class:

/*
*  TetrisShape.fx - A Tetris piece, configurable to the
*                   different shape types.  They are:
*                   I, J, L, O, S, T, and Z
*
*  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
*  to serve as a compiled JavaFX Script example.

*/
package tetris_ui;

import javafx.ui.*;
import javafx.ui.canvas.*;
import java.awt.Point;
import java.lang.System;
import tetris_model.*;

class TetrisShape extends CompositeNode {
  private static attribute squareOutlineColor = Color.BLACK;
  private static attribute squareOutlineWidth = 1;
  private attribute squareColor;

  public attribute model:TetrisModel;
 
  public attribute shapeType:TetrisShapeType on replace {
    squareLocs =
      for (block in shapeType.squarePositions) {
          new Point(block.x * model.SQUARE_SIZE,
                    block.y * model.SQUARE_SIZE)
      };
      squareColor = shapeType.squareColor;
  };

  private attribute squareLocs:Point[];
 
  public function composeNode():Node {
    Group {
      transform: bind [
        Translate.translate(model.SQUARE_SIZE * model.tetrominoHorzPos,
                            (model.a / model.SQUARE_SIZE).intValue() * model.SQUARE_SIZE),
        Rotate.rotate(model.tetrominoAngle,
                      squareLocs[0].x + model.SQUARE_SIZE / 2,
                      squareLocs[0].y + model.SQUARE_SIZE / 2)
      ]
      content: bind [
        for (squareLoc in squareLocs) {
          Rect {
            x: squareLoc.x
            y: squareLoc.y
            width: bind model.SQUARE_SIZE
            height: bind model.SQUARE_SIZE
            fill: bind squareColor
            stroke: squareOutlineColor
            strokeWidth: squareOutlineWidth
          }
        }
      ]
    };
  }
}

The newly introduced TetrisPlayingField custom graphical component that manages the area in which the tetrominoes fall:

/*
*  TetrisPlayingField.fx -
*  A custom graphical component that is the UI for the
*  playing field.
*
*  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
*  to serve as a compiled JavaFX Script example.

*/
package tetris_ui;

import javafx.ui.*;
import javafx.ui.canvas.*;
import java.lang.System;
import tetris_model.*;

class TetrisPlayingField extends CompositeNode {
  public attribute model:TetrisModel;

  public function composeNode():Node {
      Group {
        content: [
          Rect {
            x: 0
            y: 0
            width: model.NUM_COLS * model.SQUARE_SIZE
            height: model.NUM_ROWS * model.SQUARE_SIZE
            strokeWidth: 1
            stroke: Color.BLACK
            fill: Color.WHITE
          },
          TetrisShape {
            model: model
            shapeType: bind model.activeShapeType
          }
        ]
      }
  }
}

The TetrisShapeType class defines the tetromino types, and I decided that it is really part of the model, so I moved it to the tetris_model package:

/*
*  TetrisShapeType.fx - A Tetris shape type, which are
*                       I, J, L, O, S, T, and Z
*
*  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
*  to serve as a compiled JavaFX Script example.

*/
package tetris_model;

import javafx.ui.*;
import java.awt.Point;
import java.lang.Math;

/**
* This class contains the model information for each type
* of tetromino (Tetris piece).
*/
public class TetrisShapeType {
  public attribute id: Integer;
  public attribute name: String;
 
  /**
   * A sequence containing positions of each square in a
   * tetromino type.  The first element in the sequence is
   * the one around which the tetromino will rotate.
   * Note that the "O" tetromino type doesn't rotate.
   */
  public attribute squarePositions:Point[];
 
  public attribute allowRotate:Boolean = true;
 
  public attribute squareColor:Color;

  /** The "I" shape (four squares in a straight line) */
  public static attribute I =
    TetrisShapeType {
      id: 1
      name: "I"
      squarePositions: [
        new Point(1, 0),
        new Point(0, 0),
        new Point(2, 0),
        new Point(3, 0)
      ]
      squareColor: Color.RED
    };

  /** The "T" shape (looks like a stout "T") */
  public static attribute T =
    TetrisShapeType {
      id: 2
      name: "T"
      squarePositions: [
        new Point(1, 0),
        new Point(0, 0),
        new Point(2, 0),
        new Point(1, 1)
      ]
      squareColor: Color.GREEN
    };

  /** The "L" shape (looks like an "L") */
  public static attribute L =
    TetrisShapeType {
      id: 3
      name: "L"
      squarePositions: [
        new Point(1, 0),
        new Point(0, 0),
        new Point(2, 0),
        new Point(0, 1)
      ]
      squareColor: Color.MAGENTA
    };

  /** The "J" shape (looks sort of like a "J", but
   *  more like a backwards "L") */
  public static attribute J =
    TetrisShapeType {
      id: 4
      name: "J"
      squarePositions: [
        new Point(1, 1),
        new Point(1, 0),
        new Point(1, 2),
        new Point(0, 2)
      ]
      squareColor: Color.YELLOW
    };

  /** The "S" shape (looks sort of like an "S") */
  public static attribute S =
    TetrisShapeType {
      id: 5
      name: "S"
      squarePositions: [
        new Point(1, 0),
        new Point(2, 0),
        new Point(0, 1),
        new Point(1, 1)
      ]
      squareColor: Color.CYAN
    };

  /** The "Z" shape (looks sort of like an "Z") */
  public static attribute Z =
    TetrisShapeType {
      id: 6
      name: "Z"
      squarePositions: [
        new Point(1, 0),
        new Point(0, 0),
        new Point(1, 1),
        new Point(2, 1)
      ]
      squareColor: Color.ORANGE
    };

  /** The "O" shape (looks sort of like an "O", but
   *  more like a square) */
  public static attribute O =
    TetrisShapeType {
      id: 7
      name: "O"
      squarePositions: [
        new Point(0, 0),
        new Point(0, 1),
        new Point(1, 0),
        new Point(1, 1)
      ]
      squareColor: Color.BLUE //TODO:Research official color
      allowRotate: false
    };
   
  /**
   * A sequence of the shape types for use in generating a
   * random shape type.
   */
  private static attribute allShapeTypes:TetrisShapeType[];
 
  /**
   * A function that returns a random TetrisShapeType
   */
  public static function randomShapeType():TetrisShapeType {
    if (sizeof allShapeTypes <= 0) {
      insert I into allShapeTypes;
      insert T into allShapeTypes;
      insert L into allShapeTypes;
      insert J into allShapeTypes;
      insert S into allShapeTypes;
      insert Z into allShapeTypes;
      insert O into allShapeTypes;
    }
    allShapeTypes[(Math.random() * sizeof allShapeTypes) as Integer]
  }
}

And finally, here's the TetrisModel class.  I added several attributes to this class, mainly to serve as model variables for the new TetrisPlayingField class.

/*
*  TetrisModel.fx - The model behind the Tetris UI
*
*  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
*  to serve as a compiled JavaFX Script example.

*/
package tetris_model;

import javafx.ui.animation.*;
import java.lang.System;
import com.sun.javafx.runtime.PointerFactory;
import java.lang.Double;

public class TetrisModel {
  /** Size of each tetromino in pixels */
  public static attribute SQUARE_SIZE = 20;
 
  /** Number of rows in the playing field */
  public static attribute NUM_ROWS = 20;
 
  /** Number of columns in the playing field */
  public static attribute NUM_COLS = 10;

  /**
   * Sequence of objects that represent the
   * type of shapes in each playing field cell
   */
  public attribute fieldCells:TetrisShapeType[];
 
  /**
   * The active tetromino shape type.
   */
  public attribute activeShapeType:TetrisShapeType;
 
  public attribute running:Boolean = true;
  public attribute a:Integer on replace {
    if (a >= stopValue) {
      activeShapeType = TetrisShapeType.randomShapeType();
      tetrominoAngle = 0;
    }
  };
  private attribute pf = PointerFactory {};
  private attribute bpa = bind pf.make(a);
  private attribute pa = bpa.unwrap();
  private attribute interpolate = NumberValue.LINEAR;
  private attribute stopValue = 370;
  public attribute t =
    Timeline {
      keyFrames: [
        KeyFrame {
          keyTime: 0s;
          keyValues:
            NumberValue {
              target: pa;
              value: 0;
              interpolate: bind interpolate
            }
        },
        KeyFrame {
          keyTime: 10s;
          keyValues:
            NumberValue {
              target: pa;
              value: stopValue
              interpolate: bind interpolate
            }
        }
      ]
      repeatCount: Double.POSITIVE_INFINITY
    };
  public attribute tetrominoAngle:Number;
  public attribute tetrominoHorzPos:Number = (NUM_COLS / 2) as Integer;

  public function rotate90():Void {
    if (activeShapeType.allowRotate) {
      (tetrominoAngle += 90) % 360;
    }
  }

  public function moveLeft():Void {
    if (tetrominoHorzPos > 0) {
      tetrominoHorzPos--;
    }
  }
 
  public function moveRight():Void {
    //TODO: Need to allow pieces to bump up against the
    //      right side.  For now, hardcode 3.
    if (tetrominoHorzPos < NUM_COLS - 3) {
      tetrominoHorzPos++;
    }
  }
}

Some Remaining Functionality

There is still much to do in order to make this a fully functioning Tetris game.  For example, the model needs to:

  • Keep track of which cells in the playing field are empty, and which hold tetromino debris.
  • Know when a tetromino has landed on the bottom (or on tetromino debris), so that it can stop falling.
  • Delete the tetromino debris from a filled up row, and allow the tetromino debris lying above to collapse into the vacated row.
  • Use the rotation state of the currently falling tetromino to allow it to accurately bump up against the left and right sides.

Also, functionality needs to be added that displays the score, shows the next tetromino shape to fall, etc.  Have fun running, and examining, this code!

A JavaFXpert BootCamp Question:

I'm considering offering the JavaFXpert BootCamp directly after JavaOne (all day Saturday, Sunday afternoon, and all day Monday, in the San Jose/San Fransisco area).  The dates would be May 10,11 and 12.  The plan would be to allow up to 50 students in the BootCamp, which would allow me to reduce the price for that particular class.  For details on the content of the JavaFXpert BootCamp, please see http://bootcamp.JavaFXpert.com
Please send me an email to jim.weaver at lat-inc.com if you'd consider attending this, so I can decide whether to offer this post-JavaOne edition of the BootCamp.

Javafxpertbootcamp300x250bannerindy

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

February 22, 2008

Let's Create a Tetris Game in Compiled JavaFX Script

I thought it would be fun to spend a few posts creating a Tetris game in compiled JavaFX Script together.  Today I made a rough start on it, and if you promise not to laugh, I'll show you the humble beginnings.  Here's a screenshot of its current status:

Tetris_1

In Tetris, there are several types of tetrominoes, each having a letter that it resembles.  The four buttons on the left represent four of these shapes.  When you press one of these buttons, the corresponding tetromino appears at the top and begins moving down the screen.  When you click the Rotate button, the tetromino rotates to the right, and the Left/Right buttons move the tetromino left and right, respectively.

The code is contained in four FX program files, and needs some refactoring already. :-)  Before showing you the code in its current state, I'd like to point out a couple of helpful things: 

  • As explained in the Spinning Wheel post, the Key Frame animation syntax that you see here will become much less verbose as the JavaFX Script compiler team continues to address animation. 
  • JavaFX Script programs should always be designed with the UI binding to a model.  In this program, the model is represented in one class named TetrisModel, located in the FX file of the same name.  You may find it helpful to take a look the Creating a Compiled JavaFX Script Program with Multiple FX Source Files post to see a Hello World style program that has more than one FX file.  Please notice the package statments in this Tetris program, as that influences where you need to put the source files and how you build them.
  • You can obtain the JavaFX Compiler by following the instructions in the Obtaining the OpenJFX Script Compiler post.

The Source Code (so far)

Here's the main program, named TetrisMain.fx, that declaratively expresses the UI, and starts things up:

/*
*  TetrisMain.fx - The main program for a compiled JavaFX Script Tetris game
*
*  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
*  to serve as a compiled JavaFX Script example.
*/
package tetris_ui;

import javafx.ui.*;
import javafx.ui.canvas.*;
import java.lang.System;
import tetris_model.*;

Frame {
  var model =
    TetrisModel {
      
    }
  var canvas:Canvas
  width: 480
  height: 500
  title: "TetrisJFX"
  background: Color.WHITE
  content:
    BorderPanel {
      center:
        canvas = Canvas {}
      bottom:
        FlowPanel {
          content: [
            Button {
              text: "I"
              action:
                function() {
                  canvas.content = [];
                  insert
                  TetrisShape {
                    model: model
                    shapeType: TetrisShapeType.I
                  }
                  into canvas.content; 
                  model.t.start();
                }
            },
            Button {
              text: "T"
              action:
                function() {
                  canvas.content = [];
                  insert
                  TetrisShape {
                    model: model
                    shapeType: TetrisShapeType.T
                  }
                  into canvas.content; 
                  model.t.start();
                }
            },
            Button {
              text: "L"
              action:
                function() {
                  canvas.content = [];
                  insert
                  TetrisShape {
                    model: model
                    shapeType: TetrisShapeType.L
                  }
                  into canvas.content; 
                  model.t.start();
                }
            },
            Button {
              text: "S"
              action:
                function() {
                  canvas.content = [];
                  insert
                  TetrisShape {
                    model: model
                    shapeType: TetrisShapeType.S
                  }
                  into canvas.content; 
                  model.t.start();
                }
            },
            Button {
              text: "Rotate"
              action:
                function() {
                  model.rotate90();
                }
            },
            Button {
              text: "Left"
              action:
                function() {
                  model.moveLeft();
                }
            },
            Button {
              text: "Right"
              action:
                function() {
                  model.moveRight();
                }
            }
          ]
        }
    }
  visible: true
  onClose:
    function():Void {
      System.exit(0);
    }
}

I made the TetrisShape class a custom graphical component.  Therefore, it is a subclass of the CompositeNode class, and overrides the composeNode function:

/*
*  TetrisShape.fx - A Tetris piece, configurable to the
*                   different shape types.  They are:
*                   I, J, L, O, S, T, and Z
*
*  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
*  to serve as a compiled JavaFX Script example.

*/
package tetris_ui;

import javafx.ui.*;
import javafx.ui.canvas.*;
import java.awt.Point;
import java.lang.System;
import tetris_model.*;

class TetrisShape extends CompositeNode {
  private static attribute squareOutlineColor = Color.BLACK;
  private static attribute squareOutlineWidth = 2;
  private attribute squareColor;

  public attribute model:TetrisModel;
  public attribute shapeType:TetrisShapeType on replace {
    if (shapeType == TetrisShapeType.I) {
      squareLocs = [];
      insert new Point(0, model.SQUARE_SIZE * 1) into squareLocs;
      insert new Point(0, 0) into squareLocs;
      insert new Point(0, model.SQUARE_SIZE * 2) into squareLocs;
      insert new Point(0, model.SQUARE_SIZE * 3) into squareLocs;
      squareColor = Color.RED;
    }
    else if (shapeType == TetrisShapeType.T) {
      squareLocs = [];
      insert new Point(model.SQUARE_SIZE * 1, 0) into squareLocs;
      insert new Point(0, 0) into squareLocs;
      insert new Point(model.SQUARE_SIZE * 2, 0) into squareLocs;
      insert new Point(model.SQUARE_SIZE * 1, model.SQUARE_SIZE * 1) into squareLocs;
      squareColor = Color.GREEN;
    }
    else if (shapeType == TetrisShapeType.L) {
      squareLocs = [];
      insert new Point(0, model.SQUARE_SIZE * 1) into squareLocs;
      insert new Point(0, 0) into squareLocs;
      insert new Point(0, model.SQUARE_SIZE * 2) into squareLocs;
      insert new Point(model.SQUARE_SIZE * 1, model.SQUARE_SIZE * 2) into squareLocs;
      squareColor = Color.MAGENTA;
    }
    else if (shapeType == TetrisShapeType.S) {
      squareLocs = [];
      insert new Point(model.SQUARE_SIZE * 1, 0) into squareLocs;
      insert new Point(model.SQUARE_SIZE * 2, 0) into squareLocs;
      insert new Point(0, model.SQUARE_SIZE * 1) into squareLocs;
      insert new Point(model.SQUARE_SIZE * 1, model.SQUARE_SIZE * 1) into squareLocs;
      squareColor = Color.CYAN;
    }
  }
 
  private attribute squareLocs:Point[];
 
  public function composeNode():Node {
    return
      Group {
        transform: bind [
          Translate.translate(model.SQUARE_SIZE * model.tetrominoHorzPos,
                              (model.a / model.SQUARE_SIZE).intValue() * model.SQUARE_SIZE),
          Rotate.rotate(model.tetrominoAngle,
                        squareLocs[0].x + model.SQUARE_SIZE / 2,
                        squareLocs[0].y + model.SQUARE_SIZE / 2)
        ]
        content: [
          for (squareLoc in squareLocs) {
            Rect {
              x: bind squareLoc.x
              y: bind squareLoc.y
              width: bind model.SQUARE_SIZE
              height: bind model.SQUARE_SIZE
              fill: bind squareColor
              stroke: squareOutlineColor
              strokeWidth: squareOutlineWidth
            }
          }
        ]
      };
  }
}

The TetrisShapeType class defines the tetromino types:

/*
*  TetrisShapeType.fx - A Tetris shape type, which are
*                       I, J, L, O, S, T, and Z
*
*  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
*  to serve as a compiled JavaFX Script example.

*/
package tetris_ui;

import javafx.ui.*;

class TetrisShapeType {
  public attribute id: Integer;
  public attribute name: String;

  public static attribute O =
    TetrisShapeType {id: 0, name: "O"};
  public static attribute I =
    TetrisShapeType {id: 1, name: "I"};
  public static attribute T =
    TetrisShapeType {id: 2, name: "T"};
  public static attribute L =
    TetrisShapeType {id: 3, name: "L"};
  public static attribute S =
    TetrisShapeType {id: 4, name: "S"};
}

And finally, here's a model class, named TetrisModel:

/*
*  TetrisModel.fx - The model behind the Tetris UI
*
*  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
*  to serve as a compiled JavaFX Script example.

*/
package tetris_model;

import javafx.ui.animation.*;
import java.lang.System;
import com.sun.javafx.runtime.PointerFactory;

public class TetrisModel {
  public static attribute SQUARE_SIZE = 20;
 
  public attribute a:Integer;
  private attribute pf = PointerFactory {};
  private attribute bpa = bind pf.make(a);
  private attribute pa = bpa.unwrap();
  private attribute interpolate = NumberValue.LINEAR;
  public attribute t =
    Timeline {
      keyFrames: [
        KeyFrame {
          keyTime: 0s;
          keyValues:
            NumberValue {
              target: pa;
              value: 0;
              interpolate: bind interpolate
            }
        },
        KeyFrame {
          keyTime: 20s;
          keyValues:
            NumberValue {
              target: pa;
              value: 370
              interpolate: bind interpolate
            }
        }
      ]
    };
  public attribute tetrominoAngle:Number;
  public attribute tetrominoHorzPos:Number = 10;
 
  public function rotate90():Void {
    (tetrominoAngle += 90) % 360;
  }

  public function moveLeft():Void {
    if (tetrominoHorzPos > 0) {
      tetrominoHorzPos--;
    }
  }
 
  public function moveRight():Void {
    if (tetrominoHorzPos < 20) { //TODO:Replace 10 with a calculated number
      tetrominoHorzPos++;
    }
  }

}

Compile and execute this example, and examine the code.  I'll get busy making it behave a little more like a Tetris game, and show you some progress in the next post.  Please feel free to get ahead of me, and make your own version!

JavaFX Script Boot Camp Registration Now Open!

Javafxpertbootcamp300x250bannerindy

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.  By the way, I'm scouting out the next location, so if you have suggestions for where I should offer a future BootCamp please let me know!

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

February 21, 2008

Compiled JavaFX Script Now Speaks JSON

As I explained in the XML and JSON Processing post, compiled JavaFX Script has an XML abstraction. It now has a JSON abstraction as well.  I'm sure that there will be tweaks and enhancements to both, but today's example demonstrates how to parse JSON documents.  Here's a screenshot of the application:

Json_example

Go ahead and compile the program, type in an artist, and see the table get populated with the albums from that artist.  As discussed and demonstrated in the Freebase Browser post, the freebase.com server communicates via JSON.  Today's example parses JSON streams from that server as well, sending it the artist name that you enter, and displaying the album titles in a table.  Note that the freebase.com server uses a couple of different ID schemes, so I'll give you a few artists to try:

  • u2
  • the_beatles
  • james_taylor
  • the_rolling_stones
  • the_who
  • norah_jones

Here's the code for the example.  Note: I used a "->" in this example to denote continuing a string on the next line.  When you cut and paste this code into your IDE or text editor, please remove the "->" characters and bring the next line beside that one.  This long line contains the query sent to freebase.com 

/*
*  AlbumsJSON.fx - A compiled JavaFX program that demonstrates
*                  using JSON.
*
*  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
*  to serve as a JavaFX Script example.
*/

import javafx.ui.*;
import javafx.json.*;
import java.lang.Exception;
import java.lang.System;
import java.net.URL;
import java.net.URI;
import java.io.InputStream;

class Model {
  attribute albums:String[];
  private attribute freebaseURL = "http://www.freebase.com/api/service/mqlread?queries=";
  private attribute jsonObj:JSONObject;

  function findAlbums(artist:String):Void {

    var query =
      "\{\"albums\":\{\"query\":\{\"type\":\"/music/artist\", -->
        \"id\":\"/topic/en/{artist}\",\"album\":[]}}}";
    var uri = new URI("http","www.freebase.com", "/api/service/mqlread",
                      "queries={query}", null);
    var url = uri.toURL();

    var p = new Parser();
    jsonObj = p.parse(url.openStream());

    try {
      albums =
      for (albumz in jsonObj.pairs where albumz.name == "albums",
            result in (albumz.value as JSONObject).pairs where result.name == "result",
              album in (result.value as JSONObject).pairs where album.name == "album",
                alb in (album.value as Array).array ) {
          alb.toString();
      }
    }
    catch (e:Exception) {
      MessageDialog {
        title: "Try Again"
        message: "{artist} is not a valid ID for freebase.com"
        visible: true
      }
    }
  }
}

Frame {
  var model = Model {}
  width: 480
  height: 500
  title: "JSON Example"
  background: Color.WHITE
  content:
    BorderPanel {
      center:
        FlowPanel {
          content: [
            Table {
              columns: [
                TableColumn {
                  text: "Album Name"
                }
              ]
              cells: bind for (album in model.albums)
                TableCell {
                  font:
                    Font {
                      size: 16
                    }
                  text: bind "{album}"
                  selected: false
                }
            }
          ]
        }
      bottom:
        FlowPanel {
          var artistTF:TextField
      font:
        Font {
          style: FontStyle.BOLD
          size: 16
        }
          content: [
            SimpleLabel {
              text: "Artist:"
            },
            artistTF = TextField {
              columns: 20
            },
            Button {
              text: "Find Albums"
              defaultButton: true
              action:
                function():Void {
                  model.findAlbums(artistTF.value.toLowerCase())
                }
            }
          ]
        }
    }
  visible: true
  onClose:
    function():Void {
      System.exit(0);
    }
}

Here is the JSON stream returned from freebase.com when you request norah_jones:

{
  "code" : "/api/status/ok",
  "result" : [
    {
      "album" : [
        "First Sessions",
        "Come Away With Me (Limited Edition)",
        "Come Away With Me",
        "Feels Like Home",
        "Feels Like Home (disc 1)",
        "Come Away With Me (bonus disc)",
        "Feels Like Home (bonus disc)",
        "Hamburg 2002 (WDR)",
        "Live In New Orleans",
        "Live in Virginia Beach, Va 7-13-02",
        "Special Norah Jones",
        "Sunrise",
        "Turn Me On",
        "Don't Know Why",
        "Feels Like Home (Deluxe Edition: Disc 1)",
        "Not Too Late",
        "Stay With Me",
        "First Sessions",
        "Not Too Late",
        "New York City"
      ],
      "id" : "/topic/en/norah_jones",
      "type" : "/music/artist"
    }
  ]
}

Parsing in this manner is just one of the features of the JavaFX Script JSON abstraction.  Other features include building a JSON document using object literal syntax, and SAX-like processing of the JSON stream.

JavaFX Script Boot Camp Registration Now Open!

Javafxpertbootcamp300x250bannerindy

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.  By the way, I'm scouting out the next location, so if you have suggestions for where I should offer a future BootCamp please let me know!

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

February 18, 2008

Hear Juicy Bits about J6u10 and JavaFX in Java Posse Interview

If you've got a few minutes for a silly conversation that has some revealing insights into Java SE 6 Update 10 (the Get Deployment Right release) and JavaFX, then listen to this Java Posse interview with Josh Marinacci and Rich Bair from Sun, starting at minute 42:00 in the podcast.  By the way, here are the mugshots of the Java Posse:

Javaposse_2

Right out of the gate (after an inevitable comment from someone about beer), Joshua made the rather insightful remark that "JavaFX is sort of a code word for reinventing client Java and fixing the sins of the past".  They go on to discuss some of the cool features of the new browser plug-in (according to the interview they "shot the old one in the head and buried it out back"), and about J6u10 in general.

For more background and info on J6u10, see my Java Kernel post.  Enjoy the podcast, but please don't say I didn't warn you that it contains mucho sillyness :-)

JavaFXpert BootCamp Registration Now Open

Javafxpertbootcamp300x250bannerindy

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

 

February 16, 2008

Gosling Q&A Video from JavaPolis 2007 - Has Lots to Say about JavaFX

James Gosling had a lot to say about JavaFX Script and JavaFX Moble at JavaPolis 2007.  The video of the Q&A session with James Gosling has just been posted in which he addresses audience questions, a few of which relate to JavaFX.

Goslingqaatjavapolis2008

The video is twenty-five minutes long, and there is an index that enables you to go directly to each of the questions:

Goslingqaatjavapolis2008index

Particulary relevant to JavaFX are the answers that Dr. Gosling gives to the "Where is Swing Going", "What is the future of Java in the RIA space?", and "Why would I use JavaFX instead of Flex?" questions.  It's worth a listen!

JavaFX Script Boot Camp Registration Now Open

Javafxpertbootcamp300x250bannerindy

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

February 15, 2008

Binding to a Function in Compiled JavaFX Script

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.

Bindtofunctionexample

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!

Javafxpertbootcamp300x250bannerindy

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

February 13, 2008

Excellent JavaFX Script Presentation by Matthew McCullough

Matthew McCullough of Ambient Ideas co-presented with me on JavaFX Script at the Boulder Java Users Group last evening.  Matthew's JavaFX Script presentation makes a great case for developing rich internet applications in JavaFX Script.  It discusses JRE deployment improvements as well.  There are links to the source code of his JavaFX Script examples on the second Q&A slide.

Ambientideasjavafx_presentation

His presentation also set the stage for me to dive right into code during my part of the presentation.  Mine was a subset of a JavaFX Script presentation that I delivered at JavaPolis a couple of months ago. 

By the way, we're going to do the same presentation tonight (Feb 13, 2008) at the Denver Java Users Group meeting, so if you're in the Denver area, please stop by!

JavaFX Script Boot Camp Registration Now Open!

Javafxpertbootcamp300x250bannerindy

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. 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

February 11, 2008

Trigger Happy - Sequence Triggers in Compiled JavaFX Script

In the Binding to a Sequence in Compiled JavaFX Script article, we bound a ListBox to a sequence in the model.  This binding causes the ListBox to stay in sync with the sequence.  The example in today's article builds on the previous one, introducing the concept of sequence triggers.  Here's a screenshot of the program, followed by the source code.  I'll follow up with a brief discussion.

Sequencetriggers_2

/*
*  SequenceTriggers.fx
*
*  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
*  to serve as a JavaFX Script example of using sequence triggers
*/
import javafx.ui.*;
import java.lang.System;

class Model {
  attribute entries:String[] = ["Red", "Green", "Blue", "Yellow", "Purple"]
      on replace oldValue[idxA..idxB] = newElement {
        System.out.println("replaced {oldValue} at {idxA} with {entries[idxA]}");
      };
  attribute selectedIndex:Integer = 0 on replace {
    System.out.println("selectedIndex={selectedIndex}");
  };
  attribute enteredText:String;
}
 
Frame {
  var mdl = Model {}
  title: "Sequence Triggers"
  width: 400
  height: 200
  background: Color.WHITE
  visible: true
  content:
    BorderPanel {
      center:
        FlowPanel {
          content:
            ListBox {
              cells: bind for (entry in mdl.entries)
                ListCell {
                  text: entry
                }
              selection: bind mdl.selectedIndex with inverse
              fixedCellWidth: 100
              visibleRowCount: 4
            }
        }
      bottom:
        FlowPanel {
          content: [
            TextField {
              value: bind mdl.enteredText with inverse
              columns: 10
            },
            Button {
              text: "Insert"
              defaultButton: true
              enabled: bind mdl.enteredText.trim() <> ""
              action:
                function():Void {
                  insert mdl.enteredText before mdl.entries[mdl.selectedIndex.intValue()];
                  mdl.enteredText = "";
                  System.out.println("sizeof entries={sizeof mdl.entries}");
                }
            },
            Button {
              text: "Replace"
              enabled: bind mdl.enteredText.trim() <> ""
              action:
                function():Void {
                  mdl.entries[mdl.selectedIndex.intValue()] = mdl.enteredText;
                  mdl.enteredText = "";
                }
            },
            Button {
              text: "Delete"
              enabled: bind mdl.selectedIndex >= 0
              action:
                function():Void {
                  delete mdl.entries[mdl.selectedIndex.intValue()];
                }
            }
          ]
        }
    }
}

Sequence Triggers

Triggers can be created that execute when certain operations happen to a sequence.  These operations are:

  • Inserting a slice
  • Replacing a slice
  • Deleting a slice

Note that a slice is a contiguous portion of a sequence, which can consist of one or more elements.  The program example above demonstrates the use of each of these operations, which are now handled exclusively by the replace trigger. Notice the distinction between the replace trigger that applies to slices of a sequence, and the replace trigger that applies to attributes (as shown in the selectedIndex attribute declaration).

For an in-depth explanation of of sequence replace triggers and slices, please see a writeup on the subject from the JavaFX Compiler team.

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

 

February 10, 2008

Binding to a Sequence in Compiled JavaFX Script

The JavaFX Script Compiler team has made great strides recently in the area of sequence binding.  For a refresher on sequences, see the Getting Plutoed post.  Here's an example of binding a ListBox to a sequence so that when the sequence is modified the ListBox stays in sync with the elements in the sequence.  Compile and run this example, clicking on words in the ListBox, and a entering a word followed by activating the Add button.  I'll finish up with a brief discussion on the binding aspects of this example.

Bindingtoasequence_2

/*
*  ListBoxSequenceBinding.fx
*
*  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
*  to serve as a JavaFX Script example of binding a ListBox to
*  a sequence.
*/
import javafx.ui.*;
import java.lang.System;

class Model {
  attribute entries:String[] = ["Red", "Green", "Blue", "Yellow", "Purple"];
  attribute selectedIndex:Number = 0 on replace {
    System.out.println("selectedIndex={selectedIndex}");
  };
  attribute entryToAdd:String;
}
 
Frame {
  var mdl = Model {}
  title: "Binding to a Sequence"
  width: 300
  height: 200
  background: Color.WHITE
  visible: true
  content:
    BorderPanel {
      center:
        FlowPanel {
          content:
            ListBox {
              cells: bind for (entry in mdl.entries)
                ListCell {
                  text: entry
                }
              selection: bind mdl.selectedIndex with inverse
              fixedCellWidth: 100
              visibleRowCount: 4
            }
        }
      bottom:
        FlowPanel {
          content: [
            TextField {
              value: bind mdl.entryToAdd with inverse
              columns: 10
            },
            Button {
              text: "Add"
              defaultButton: true
              enabled: bind mdl.entryToAdd.trim() <> ""
              action:
                function() {
                  insert mdl.entryToAdd into mdl.entries;
                  mdl.entryToAdd = "";
                  System.out.println("sizeof entries={sizeof mdl.entries}");
                }
            }
          ]
        }
    }
}

Binding Aspects of this Program

The main purpose of this post is to teach you about binding to a sequence, so I'll point that out first:  As shown in the following excerpt from the program, the cells attribute of the ListBox is bound to the entries sequence contained in the model:

  cells: bind for (entry in mdl.entries)
    ListCell {
      text: entry
    }

To accomplish this bind, we're enlisting the help of the for expression whose value is a sequence of ListCell instances.  Each of these instances has an element of the entries sequences assigned to its text attribute.  As the entries sequence is modified (for example by adding an element) the for expression is re-eavaluated, causing the ListBox to be dynamically updated.  To learn more about the for expression, see this post.

Another bind in this program keeps the TextField in sync with the entryToAdd attribute in the model, as shown in this expert:

  TextField {
    value: bind mdl.entryToAdd with inverse
    columns: 10
  },

This is a bi-directional bind, as signified by the with inverse keywords.  There is another UI component that binds with the entryToAdd attribute, which causes the OK button to be enabled only when the TextField contains text:

  Button {
    text: "Add"
    defaultButton: true
    enabled: bind mdl.entryToAdd.trim() <> ""
    ...code omitted...
  }

Lastly, the following bind causes the selected attribute of the ListBox to be in sync with the selectedIndex attribute of the model.  To demonstrate this, its value is printed to the console in the replace trigger, which is executed whenever the value changes.

As you can see, binding UI components to a model is a very powerful concept that greatly simplifies application development.  As always, please post a comment if you have any questions.

JavaFX Script Boot Camp Announcement

Javafxpertbootcamp300_x_250_banne_2

As a heads-up, I will be offering a JavaFX Script 2.5 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 will open soon, and for this pilot class I am accepting 12 students.  The cost of this pilot class will be 900 USD per student. Additional students from the same organization 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).  The prerequisite for the class will be the completion of a JavaFX Script programming assignment that I'll post soon to this weblog.  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