« December 2007 | Main | February 2008 »

January 2008

January 31, 2008

Want to Learn Compiled JavaFX Script? New Tutorial Available

If you'd like to learn to develop programs in compiled JavaFX Script, there is a new resource available.  It is an 18 part tutorial-style series entitled Creating Rich Internet Applications With Compiled JavaFX Script Technology, and is available on the java.sun.com home page.  This series will unfold over the next six months, so now is a great time to get started on the first lesson!

Javasuncom_compiledjavafxscripttuto

Important Note: There is currently a misprint in the code example in that article.  Here is the correct code, and the article will be corrected shortly:

/*
*  HelloCompiledJavaFX.fx - A "Hello World" style, but slightly more
*                           sophisticated, compiled JavaFX Script example
*
*  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
*  to serve as a compiled JavaFX Script example.
*/
package mypackage;

import javafx.ui.*;
import javafx.ui.canvas.*;

Frame {
  title: "Hello Rich Internet Applications!"
  width: 550
  height: 200
  background: Color.WHITE
  visible: true
  content:
    BorderPanel {
      top:
        FlowPanel {
          content:
            Button {
              text: "Click Me"
              action:
                function():Void {
                  MessageDialog {
                    title: "JavaFX Script Rocks!"
                    // This string has a newline in the source code
                    message: "JavaFX Script is Simple, Elegant,
and Leverages the Power of Java"
                    visible: true
                  }
                }
            }
        }
      center:
        Canvas {
          content:
            Text {
              font:
                Font {
                  faceName: "Sans Serif"
                  style: FontStyle.BOLD
                  size: 24
                }
              x: 20
              y: 40
              stroke: Color.BLUE
              fill: Color.BLUE
              content: "JavaFX Script Makes RIA Development Easy"
            }
        }
    }
}

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

January 29, 2008

Spinning Wheel Got to Go 'Round - A Compiled JavaFX Script Example

At Java Mobile and Embedded Developer Days last week I met a lot of great people.  One of these was Kevin Nilson, the leader of the Silicon Valley Web Developer JUG.  He relayed the idea (based upon something used by the No Fluff Just Stuff gang) of creating an application in JavaFX Script that consists of a wheel that has the names of JUG attendees on it.  The wheel would revolve and land on a name, who would then receive a prize.  Today's post is a first cut at this, which also demonstrates several compiled JavaFX Script features.  Here's a screenshot of the application:

Winnerwheeljfx_3  

When the program starts up, a wheel with some fictitious names appears.  The user can click the dot in the center of the circle and enter a list of names (up to 60) in a dialog box, shown below:

Winnerwheeljfx_dialog

Clicking the OK button causes the names entered to appear in the wheel, as shown in the screenshot above.  Clicking the arrow on the left causes the wheel to spin, landing on a name, and showing a dialog box with the winner:

Winnerwheeljfx_winnerdialog

Presumably, since the JUG is in California, the rock bands in this list regularly attend the Silicon Valley JUGs :-) 

The Source Code with a Caveat

Here's the source code for the application, but please keep in mind that the Key-Frame animation capability in compiled JavaFX Script is still under development.  The syntax planned is very succinct, as referred to in the Key-Frame Animation post in Chris Oliver's Weblog.  The syntax shown here uses Key-Frame animation, but is more verbose.  It also exposes an underlying compiled JavaFX Script feature (pointers), that won't be exposed when the Key-Frame animation implementation is fully baked.

With that in mind, here is the source for our WinnerWheelJFX custom component.  Notice that it extends CompositeNode, so it is a graphical component:

/*
*  WinnerWheelJFX.fx - A wheel with names on it that spins
*                      and lands on a random name.  This will
*                      be used initially by Java Users Groups
*                      to give a away door prizes.  It will be
*                      improved upon over time, serving as an
*                      example of compiled JavaFX 2D graphics
*                      and animation.
*
*                      Note: The compiled JavaFX Script Key-Frame
*                      implementation isn't complete, so the
*                      animation syntax is more verbose than it
*                      will be in the near future.
*
*  Initially developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
*/
import javafx.ui.*;
import javafx.ui.animation.*;
import javafx.ui.canvas.*;
import java.io.BufferedReader;
import java.io.StringReader;
import java.lang.Math;
import java.lang.System;
import com.sun.javafx.runtime.PointerFactory;

public class WinnerWheelJFX extends CompositeNode {
  public attribute names:String[];
  public attribute chosenName:String;
  public attribute chosenIdx:Integer;
  public attribute running:Boolean = true;
 
  private static attribute maxNames = 60;
  private attribute dlg:Dialog;
  private attribute rand:Number;
  private attribute stopValue = 1100;
  private attribute a:Integer on replace (olda) {
    chosenIdx = (((a * rand) % 360) / 360.0 * sizeof names).intValue();
    chosenName = names[chosenIdx];
    if (a >= stopValue) {
      running = false;
      MessageDialog {
        title: "And the Winner Is..."
        visible: true
        message: chosenName
      }
    }
  }
  private attribute pf = PointerFactory {};
  private attribute bpa = bind pf.make(a);
  private attribute pa = bpa.unwrap();
  private attribute interpolate = NumberValue.LINEAR;
  private attribute t =
    Timeline {
      keyFrames: [
        KeyFrame {
          keyTime: 0s;
          keyValues:
            NumberValue {
              target: pa;
              value: 0;
              interpolate: bind interpolate
            }
        },
        KeyFrame {
          keyTime: 10s;
          keyValues:
            NumberValue {
              target: pa;
              value: 700;
              interpolate: bind interpolate
            }
        },
        KeyFrame {
          keyTime: 20s;
          keyValues:
            NumberValue {
              target: pa;
              value: stopValue
              interpolate: bind interpolate
            }
        }
      ]
    };

  public function spin() {
    running = true;
    rand = Math.random() + .5;
    interpolate = NumberValue.LINEAR;
    t.start();
  }
  public function composeNode():Node {
    var margin = 20;
    var canvas = getCanvas();
    var cX = bind canvas.width / 2;
    var cY = bind canvas.height / 2;
    var rad = bind Math.min(cX, cY) - margin;
    var origX = bind cX - rad;
    var origY = bind cY - rad;
    return
      Group {
        content: [
          Polygon {
            var spinFillColor = Color.PURPLE
            points: bind [
              cX - rad,
              cY,
              origX / 2,
              cY - (origX / 4),
              origX / 2,
              cY + (origX / 4)
            ]
            cursor: Cursor.HAND
            fill: bind spinFillColor
            onMouseEntered:
              function(cme:CanvasMouseEvent):Void {
                spinFillColor = Color.YELLOW;
              }
            onMouseExited:
              function(cme:CanvasMouseEvent):Void {
                spinFillColor = Color.PURPLE;
              }
            onMouseClicked:
              function(cme:CanvasMouseEvent):Void {
                spin();
              }
          },
          Circle {
            var editFillColor = Color.RED
            cx: bind cX
            cy: bind cY
            radius: bind rad / 30
            cursor: Cursor.HAND
            fill: bind editFillColor
            onMouseEntered:
              function(cme:CanvasMouseEvent):Void {
                editFillColor = Color.BLUE;
              }
            onMouseExited:
              function(cme:CanvasMouseEvent):Void {
                editFillColor = Color.RED;
              }
            onMouseClicked:
              function(cme:CanvasMouseEvent):Void {
                dlg = Dialog {
                  var ta = TextArea {
                    rows: 10
                    columns: 20
                    background: Color.WHITE
                    text: ""
                  }
                  modal: true
                  title: "Enter Up to {maxNames} Names"
                  visible: true
                  content: ta
                  buttons: [
                    Button {
                      text: "OK"
                      defaultButton: true
                      action:
                        function():Void {
                          names = [];
                          var peopleStr:String = ta.text;
                          var br = new BufferedReader((new StringReader(peopleStr)));
                          var line:String;
                          var i = 0;
                          while ((line = br.readLine()) <> null and i <= maxNames) {
                            insert line into names;
                            i++;
                          }
                          dlg.hide();
                        }
                    },
                    Button {
                      text: "Cancel"
                      defaultCancelButton: true
                      action:
                        function():Void {
                          dlg.hide();
                        }
                    }
                  ]
                };
              }
          },
          Group {
            transform: bind [
              Rotate.rotate(if (running) (a * rand) % 360
                            else chosenIdx.doubleValue() / sizeof names * 360.0, cX, cY)
            ]
            content: bind [
              for (name in names)
                Text {
                  transform: [
                    Rotate.rotate(((sizeof names - indexof name).doubleValue() /
                                    sizeof names * 360) % 360, cX, cY)
                  ]
                  font:
                    Font {
                      face: FontFace.SANSSERIF
                      size: 16
                      style: FontStyle.BOLD
                    }
                  fill: if ((indexof name % 3) == 1) Color.RED
                        else if ((indexof name % 3) == 2) Color.BLUE
                             else Color.GREEN
                  x: cX - rad + 5
                  y: cY
                  content: "{name}"
                }
            ]
          }
        ]
      };
  }
}

Here's a sample program that uses the WinnerWheelJFX component:

/*
*  WinnerWheelExample.fx - A JavaFX example of using a custom component
*
*  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
*/

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

Frame {
  var winnerWheel =
    WinnerWheelJFX {
      names: [
        "Aaaaaaaa Aaaaaa",
        "Bbbbbbbb Bbbbbb",
        "Cccccccc Cccccc",
        "Dddddddd Dddddd",
        "Eeeeeeee Eeeeee",
        "Ffffffff Ffffff",
        "Gggggggg Gggggg",
        "Hhhhhhhh Hhhhhh",
        "Iiiiiiii Iiiiii",
        "Jjjjjjjj Jjjjjj",
        "Kkkkkkkk Kkkkkk",
        "Llllllll Llllll",
        "Mmmmmmmm Mmmmmmmmmmm"
      ]
    }
  title: "Winner Wheel JFX"
  height: 750
  width: 800
  resizable: false
  visible: true
  content:
    Canvas {
      background: Color.WHITE
      content: winnerWheel
    }
  onClose:
    function():Void {
      System.exit(0);
    }
}

Compiling and Running this Example

Use the normal commands to compile and run this example, but since there are two source files, to compile them you'll want to use a wildcard as shown below:

javafxc *.fx

To run the example, use the following command:

javafx WinnerWheelExample

Give it a whirl!

JavaFX Script Boot Camp Announcement

Javafx_bootcamp_indy_apr_911

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!

More details to follow,
Jim Weaver
JavaFX Script: Dynamic Java Scripting for Rich Internet/Client-side Applications

Immediate eBook (PDF) download available at the book's Apress site

January 25, 2008

Seeing Spots at Java Mobile & Embedded Developer Days

The Java Mobile & Embedded Developer Days sessions are wrapping up, and I've found this conference to be a very informative and worthwhile experience  I'll write a post soon about the details of JavaFX Mobile that were presented.  In this post, however, I'd like to share with you about a contest that occurred during the conference which ended with three other attendees and me each winning a Sun SPOT Java Development Kit.  See Roger Brinkley's blog post and Terrence Barr's blog post about the contest.  Roger and Terrence dreamed up and conducted the conference in a suburb manner!

Sunspotdeveloperkit

If you take a look at the Sun SPOT World site, you'll see that Sun SPOT (Small Programmable Object Technology) is a technology that includes a small device with a Java Virtual Machine, various sensors, and other on-board features.  During the conference, the attendees were invited to participate in the contest by answering the question: "What cool thing would you develop if you had a Sun SPOT?"  My answer was that I would create programs in JavaFX Script that are geared toward helping kids become interested in learning computer programming.  One such program, for example, could communicate with Sun SPOT devices mounted in toy cars.

To help the judges (who were the other conference attendees) visualize the idea, I developed a quick (it took less than one hour) prototype in JavaFX Script of a user interface that would be appropriate for a young child.  As illustrated in the screenshot below, you can drag the icons from the right side of the screen and put them together into a sequence that you want the model car to execute (in this case, travel straight, honk the horn, and turn left).

Sunspots4kids_2

When the user clicks the Go button, the program would communicate with the Sun SPOT in the small vehicle.  In this prototype, when you press Go, a dialog box appears that lists some of the features of the proposed application, shown below:

Sunspots4kids_dialog

 

Here's the source code for the prototype:

/*
*  SunSpots4Kids.fx
*
*  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
*  to serve as a compiled JavaFX Script example to try to win
*  a Sun SPOT Development Kit at Java Mobile & Embedded Developer Days
*/

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

class ActionModel {
  attribute x:Number;
  attribute y:Number;
}

Frame {
  var dlg:Dialog
  title: "SunSpots4Kids"
  width: 820
  height: 700
  background: Color.WHITE
  visible: true
  content:
    BorderPanel {
      top:
        Canvas {
          content:
            Text {
              content: "SunSpots4Kids: Baby You Can Drive My Car"
              x: 20
              y: 20
              fill: Color.ORANGE
              stroke: Color.RED
              strokeWidth: 3
              font:
                Font {
                  face: FontFace.COMIC_SANS_MS
                  style: FontStyle.BOLD
                  size: 36
                }
            }
        }
      center:
        Canvas {
          content:
            Group {
              transform: Translate.translate(680, 40)
              content: [
                ImageView {
                  var toyHorn =
                    ActionModel {
                      x: 0
                      y: 0
                    }
                  transform: bind [
                    Translate.translate (toyHorn.x, toyHorn.y),
                  ]
                  image:
                    Image {
                      url: "file:images/toy_horn.jpg"
                    }
                  onMouseDragged: function(mEvt) {
                    toyHorn.x += mEvt.localDragTranslation.x;
                    toyHorn.y += mEvt.localDragTranslation.y;
                  }
                },
               
               
                Group {
                  var bottomLeftCurve =
                    ActionModel {
                      x: 0
                      y: 80
                    }
                  content: [
                    Arc {
                      height: 120
                      width: 120
                      startAngle: 90
                      length: 90
                      closure: ArcClosure.PIE
                      stroke: Color.BLUE
                      fill: Color.BLUE
                      strokeWidth: 5
                    },
                    Arc {
                      x: 30
                      y: 30
                      height: 60
                      width: 60
                      startAngle: 90
                      length: 90
                      closure: ArcClosure.PIE
                      stroke: Color.WHITE
                      fill: Color.WHITE
                      strokeWidth: 5
                    },
                  ]
                  transform: bind [
                    Translate.translate (bottomLeftCurve.x, bottomLeftCurve.y),
                  ]
                  onMouseDragged: function(mEvt) {
                    bottomLeftCurve.x += mEvt.localDragTranslation.x;
                    bottomLeftCurve.y += mEvt.localDragTranslation.y;
                  }
                },
                Group {
                  var topLeftCurve =
                    ActionModel {
                      x: 0
                      y: 150
                    }
                  content: [
                    Arc {
                      height: 120
                      width: 120
                      startAngle: 270
                      length: 90
                      closure: ArcClosure.PIE
                      stroke: Color.BLUE
                      fill: Color.BLUE
                      strokeWidth: 5
                    },
                    Arc {
                      x: 30
                      y: 30
                      height: 60
                      width: 60
                      startAngle: 270
                      length: 90
                      closure: ArcClosure.PIE
                      stroke: Color.WHITE
                      fill: Color.WHITE
                      strokeWidth: 5
                    },
                  ]
                  transform: bind [
                    Translate.translate (topLeftCurve.x, topLeftCurve.y),
                  ]
                  onMouseDragged: function(mEvt) {
                    topLeftCurve.x += mEvt.localDragTranslation.x;
                    topLeftCurve.y += mEvt.localDragTranslation.y;
                  }
                },
                Rect {
                  var straightAway =
                    ActionModel {
                      x: 0
                      y: 300
                    }
                  width: 100
                  height: 30
                  stroke: Color.BLUE
                  fill: Color.BLUE
                  strokeWidth: 5
                  transform: bind [
                    Translate.translate (straightAway.x, straightAway.y),
                  ]
                  onMouseDragged: function(mEvt) {
                    straightAway.x += mEvt.localDragTranslation.x;
                    straightAway.y += mEvt.localDragTranslation.y;
                  }
                },
                ImageView {
                  var headLights =
                    ActionModel {
                      x: 0
                      y: 350
                    }
                  transform: bind [
                    Translate.translate (headLights.x, headLights.y),
                  ]
                  image:
                    Image {
                      url: "file:images/headlights.jpg"
                    }
                  onMouseDragged: function(mEvt) {
                    headLights.x += mEvt.localDragTranslation.x;
                    headLights.y += mEvt.localDragTranslation.y;
                  }
                },
              ]
            }
        }
      bottom:
        FlowPanel {
          content:
            Button {
              background: Color.WHITE
              borderPainted: false
              icon:
                Image {
                  url: "file:images/go.jpg"
                }
              action:
                function() {
                  dlg = Dialog {
                    modal: true
                    width: 600
                    height: 400
                    title: "SunSpot4Kids Messages"
                    visible: true
                    content:
                      Canvas {
                        content: [
                          Text {
                            content: "Age Adjusted IDE"
                            x: 20
                            y: 20
                            fill: Color.BLUE
                            stroke: Color.BLUE
                            strokeWidth: 3
                            font:
                              Font {
                                face: FontFace.COMIC_SANS_MS
                                style: FontStyle.BOLD
                                size: 28
                              }
                            },
                            Text {
                              content: "Engaging Project Templates"
                              x: 20
                              y: 100
                              fill: Color.RED
                              stroke: Color.RED
                              strokeWidth: 3
                              font:
                                Font {
                                  face: FontFace.COMIC_SANS_MS
                                  style: FontStyle.BOLD
                                  size: 28
                                }
                            },
                            Text {
                              content: " - 3D Dance Dance Revolution"
                              x: 20
                              y: 180
                              fill: Color.GREEN
                              stroke: Color.GREEN
                              strokeWidth: 3
                              font:
                                Font {
                                  face: FontFace.COMIC_SANS_MS
                                  style: FontStyle.BOLD
                                  size: 28
                            }
                          },
                        ]
                      }
                    buttons: [
                      Button {
                        text: "OK"
                        defaultButton: true
                        action:
                          function():Void {
                            dlg.hide();
                          }
                      }
                    ]
                  };
                }
            }
        }   
    }
  onClose:
    function() {
      System.exit(0);
    }
}

Humble Beginnings

As you can see, the prototype is in its beginning stages, and there is no communication with the Spun SPOT device yet.  I plan, however, to evolve this project into the stated vision, and I'll make blog entries periodically as progress in made.  I'll put compiled JavaFX Script code in each of these blog entries because the program will be written primarily in JavaFX Script.  There will also be some specific Sun SPOT related code, so if you're not interested in that technology you can skip any code or discussions related to Sun SPOT (although I hope you don't, because it will be fun!)

Thanks again to Roger Brinkley, Terrence Barr, and others at Sun who were responsible for this conference.  Stay tuned for more on the JavaFX Mobile content, but if you'd like to get ahead of me you could take a look at the session abstracts and slides.

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

January 23, 2008

AGUI Aqui? - Java Mobile & Embedded Developer Days

In yesterday's post, I tried to speak French.  The title of today's post is partially in Spanish.  I guess I'm trying to be more global :-) 

After a day of flying, I'm excited about what I'll learn today at the Java Mobile & Embedded Developer Days in Santa Clara, California!  Because there will be lots of mobile developers and speakers here, I'll be interested in gauging how much awareness and momentum has grown around AGUI (Advanced Graphics and User Interface), which is the graphical portion of the JavaFX Mobile stack.

Agui_phone

I'll have much more to say about the conference, and hopefully AGUI, in tomorrow's post.

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

January 22, 2008

Writing Multi-Lingual Applications with Compiled JavaFX Script

The Happy New Year post broached the subject of writing internationalized applications in compiled JavaFX Script, as it covered the topic of locale-dependent formatting.  This post will introduce you to the proposal for string literal translation in compiled JavaFX Script, which provides a simple and powerful mechanism to support the creation of multilingual applications.  The current version of this proposal has been implemented in compiled JavaFX Script, so I'd like to use it to change yesterday's Bag the GridBag example into a multilingual application.  The following screenshots show the UI after changing my computer's locale to French-France.  Note: I took some French in school, but am by no means a French speaker, so if you have any suggestions for alternative translations, please post a comment.


Stringtranslation

Here is the dialog box with my information filled in:

Stringtranslation_dialog


The Modified Program

As you may have read in the proposal, to indicate that a given string is translatable, you prefix the string literal with two hash characters (##).  Here is the CompiledGroupPanel.fx program from yesterday's post (renamed to StringTranslationExample.fx) with the strings that I chose to translate prefixed with ##.

/*
*  StringTranslationExample.fx
*
*  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
*  to serve as a compiled JavaFX Script example of
*  string literal translation.
*/

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

Frame {
  var dlg:Dialog
  var firstName:String
  var lastName:String
  var favoriteBand:String
  var favoriteMovie:String
  title: ##"Bag the GridBag"
  width: 400
  height: 250
  visible: true
  menubar:
    MenuBar {
      menus: [
        Menu {
          text: ##"Options"
          items: [
            MenuItem {
              text: ##"Favorites..."
              action:
                function():Void {
                  dlg = Dialog {
                    modal: true
                    title: ##"Favorite Things"
                    visible: true
                    content:
                      GroupPanel {
                        var firstNameRow = Row { alignment: Alignment.BASELINE }
                        var lastNameRow = Row { alignment: Alignment.BASELINE }
                        var favoriteBandRow = Row { alignment: Alignment.BASELINE }
                        var favoriteMovieRow = Row { alignment: Alignment.BASELINE }
                        var labelsColumn = Column {
                          alignment: Alignment.TRAILING
                        }
                        var fieldsColumn = Column {
                          alignment: Alignment.LEADING
                        }
                        rows: [
                          firstNameRow,
                          lastNameRow,
                          favoriteBandRow,
                          favoriteMovieRow
                        ]
                        columns: [
                          labelsColumn,
                          fieldsColumn
                        ]
                        content: [
                          SimpleLabel {
                            row: firstNameRow
                            column: labelsColumn
                            text: ##"First Name:"
                          },
                          TextField {
                            row: firstNameRow
                            column: fieldsColumn
                            columns: 15
                            background: Color.WHITE
                            focusTraversalKeysEnabled: true
                            value: bind firstName with inverse
                          }, 
                          SimpleLabel {
                            row: lastNameRow
                            column: labelsColumn
                            text: ##"Last Name:"
                          },
                          TextField {
                            row: lastNameRow
                            column: fieldsColumn
                            columns: 15
                            background: Color.WHITE
                            focusTraversalKeysEnabled: true
                            value: bind lastName with inverse
                          }, 
                          SimpleLabel {
                            row: favoriteBandRow
                            column: labelsColumn
                            text: ##"Favorite Band:"
                          },
                          TextField {
                            row: favoriteBandRow
                            column: fieldsColumn
                            columns: 20
                            background: Color.WHITE
                            focusTraversalKeysEnabled: true
                            value: bind favoriteBand with inverse
                          }, 
                          SimpleLabel {
                            row: favoriteMovieRow
                            column: labelsColumn
                            text: ##"Favorite Movie:"
                          },
                          TextField {
                            row: favoriteMovieRow
                            column: fieldsColumn
                            columns: 25
                            background: Color.WHITE
                            value: bind favoriteMovie with inverse
                          }
                        ]
                    }
                    buttons: [
                      Button {
                        text: ##"OK"
                        defaultButton: true
                        action:
                          function():Void {
                            System.out.println("First Name: {firstName}
Last Name: {lastName}
Favorite Band: {favoriteBand}
Favorite Movie: {favoriteMovie}");
                            dlg.hide();
                          }
                      }
                    ]
                  };
                }
            }
          ]
        }
      ]
    }
  onClose:
    function():Void {
      System.exit(0);
    }
}

The FX Properties File

One feature that makes this mechanism very easy is that the string itself is the key for the key/value pair that supplies the translated string.  Here is the FX properties file that I created for this example:

@charset "ISO-8859-1";
/*
*  StringTranslationExample_fr.fxproperties
*
*  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
*  to serve as a compiled JavaFX Script example of
*  string literal translation.
*  Note: I don't speak much French, so please suggest corrections
*/

"Bag the GridBag" = "Mettez le GridBag dans un sac"
"Options" = "Options"
"Favorites..." = "Favoris..."
"Favorite Things" = "Choses Préférées"
"First Name:" = "Prénom:"
"Last Name:" = "Dernier Nom:"
"Favorite Band:" = "Bande Préférée:"
"Favorite Movie:" = "Film Préféré:"
"OK" = "OK"

Notice that the first line in the FX properties file contains the ISO-8859-1 character set.  Also the name of an FX properties file is the same as the JavaFX Script program, with an underscore and the locale (_fr) appended to the name, and an extension of fxproperties.  If French-France is the current locale, the FX properties file named StringTranslationExample_fr.fxproperties will be consulted.  If there are no files that reflect the current locale, then the string literals from the program will be used.

JavaFX Script Boot Camp Announcement

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!

More details to follow,
Jim Weaver
JavaFX Script: Dynamic Java Scripting for Rich Internet/Client-side Applications

Immediate eBook (PDF) download available at the book's Apress site

January 21, 2008

Creating a Dialog in Compiled JavaFX Script - and Bag the GridBag

Today I'd like to show you how to create a dialog box in compiled JavaFX Script, as well as show you the GroupPanel layout component which is incredibly helpful in laying out dialog boxes.  Here are the screenshots of the program's UI, followed by the program's code.  I'll wrap up this post with a discussion that sheds some light on this example.

Compiledgrouppanel_2

When you click the Favorites menu item, the following dialog appears.  I've taken the liberty of filling out the fields:

Compiledgrouppanel_dialog

Here's the program code:

/*
*  CompiledGroupPanel.fx
*
*  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
*  to serve as a JavaFX Script example.
*/

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

Frame {
  var dlg:Dialog
  var firstName:String
  var lastName:String
  var favoriteBand:String
  var favoriteMovie:String
  title: "Bag the GridBag"
  width: 400
  height: 250
  visible: true
  menubar:
    MenuBar {
      menus: [
        Menu {
          text: "Options"
          items: [
            MenuItem {
              text: "Favorites..."
              action:
                function():Void {
                  dlg = Dialog {
                    modal: true
                    title: "Favorite Things"
                    visible: true
                    content:
                      GroupPanel {
                        var firstNameRow = Row { alignment: Alignment.BASELINE }
                        var lastNameRow = Row { alignment: Alignment.BASELINE }
                        var favoriteBandRow = Row { alignment: Alignment.BASELINE }
                        var favoriteMovieRow = Row { alignment: Alignment.BASELINE }
                        var labelsColumn = Column {
                          alignment: Alignment.TRAILING
                        }
                        var fieldsColumn = Column {
                          alignment: Alignment.LEADING
                        }
                        rows: [
                          firstNameRow,
                          lastNameRow,
                          favoriteBandRow,
                          favoriteMovieRow
                        ]
                        columns: [
                          labelsColumn,
                          fieldsColumn
                        ]
                        content: [
                          SimpleLabel {
                            row: firstNameRow
                            column: labelsColumn
                            text: "First Name:"
                          },
                          TextField {
                            row: firstNameRow
                            column: fieldsColumn
                            columns: 15
                            background: Color.WHITE
                            focusTraversalKeysEnabled: true
                            value: bind firstName with inverse
                          }, 
                          SimpleLabel {
                            row: lastNameRow
                            column: labelsColumn
                            text: "Last Name:"
                          },
                          TextField {
                            row: lastNameRow
                            column: fieldsColumn
                            columns: 15
                            background: Color.WHITE
                            focusTraversalKeysEnabled: true
                            value: bind lastName with inverse
                          }, 
                          SimpleLabel {
                            row: favoriteBandRow
                            column: labelsColumn
                            text: "Favorite Band:"
                          },
                          TextField {
                            row: favoriteBandRow
                            column: fieldsColumn
                            columns: 20
                            background: Color.WHITE
                            focusTraversalKeysEnabled: true
                            value: bind favoriteBand with inverse
                          }, 
                          SimpleLabel {
                            row: favoriteMovieRow
                            column: labelsColumn
                            text: "Favorite Movie:"
                          },
                          TextField {
                            row: favoriteMovieRow
                            column: fieldsColumn
                            columns: 25
                            background: Color.WHITE
                            value: bind favoriteMovie with inverse
                          }
                        ]
                    }
                    buttons: [
                      Button {
                        text: "OK"
                        defaultButton: true
                        action:
                          function():Void {
                            System.out.println("First name: {firstName}
Last name: {lastName}
Favorite band: {favoriteBand}
Favorite movie: {favoriteMovie}");
                            dlg.hide();
                          }
                      }
                    ]
                  };
                }
            }
          ]
        }
      ]
    }
  onClose:
    function():Void {
      System.exit(0);
    }
}

Creating a Dialog

To create and display a dialog box, we're creating an instance of the Dialog class, and setting its visible attribute to true.  We're assigning a reference to the Dialog instance to the variable named dlg, so that its hide() function can be invoked when appropriate.  A Dialog has a content attribute which defines what will appear in the dialog, and it has a buttons attribute that allows you to define the buttons and their functionality when activated.

Using the Most Excellent GroupPanel Layout Component

For some reason, I've always resisted using the GridBagLayout in Java.  I know that others have used it successfully, but I just seem to prefer using a combination of layout managers to get the job done.  One especially thorny layout task for me (using either approach) has been creating a column of labels that have corresponding UI components beside them, all neatly lined up as shown in the previous screenshot.  The GroupPanel layout component excels at this task, allowing you to define named rows and columns, and their alignments.  You then place each UI component at a given row and column.

This example uses the bind with inverse keywords to keep the UI in sync with the model (which in this simple example program is some variables defined in the declarative expression).  To demonstrate that the binding works, the values that you enter in the dialog box are printed to the console when you active the OK button in the dialog box.

JavaFX Script Boot Camp Announcement

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!

More details to follow,
Jim Weaver
JavaFX Script: Dynamic Java Scripting for Rich Internet/Client-side Applications

Immediate eBook (PDF) download available at the book's Apress site

January 17, 2008

I Hear Voices: Chet Haase on JavaFX

Take a look at this InfoQ interview about JavaFX with Chet Haase, the lead author of "Filthy Rich Clients".  Please check out his book as well, because it will help prepare you for the transition to rich internet application development which I think will very quickly become pervasive this year (2008).

Filthyrichclients_2


JavaFX Script Boot Camp Announcement

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!

More details to follow,
Jim Weaver
JavaFX Script: Dynamic Java Scripting for Rich Internet/Client-side Applications
Immediate eBook (PDF) download available at the book's Apress site

January 16, 2008

Java Kernel: The Final Piece of the JRE Deployment Puzzle is in Place!

As mentioned in previous posts, and in Weiqi Gao's Game Changing Move post, the purpose of Java SE 6 Update N is to solve the JRE deployment and performance issues that have hindered the adoption of Java rich clients.  This is great news for JavaFX Script as it is dependent upon the JRE (Java Runtime Environment) being present on the client machine.  Java Kernel, which was the remaining feature of Java SE 6 Update N to be implemented, is now in place.  This feature allows the JRE to be segmented into small bundles, each of which downloads and installs quickly.  The first bundles provides just enough of the JRE to run a simple "Hello World" program.  Java Kernel will then proactively download and install the remaining bundles.  The major benefit of this approach is that the user doesn't have to wait for a large JRE download before running a Java or JavaFX Script program, because the bundles that are needed by the program are downloaded in the background as the program is starting up.  Note: When I tried this, 35 bundles were downloaded, ranging in size from 52 KB to 2610 KB.  Here is the message box that appeared when I invoked a JavaFX Script application via Java Web Start just after the Java Kernel had been installed:

Javakerneladdionalcomponentsneede_2

Please also note that even if a Java or JavaFX program is not invoked, the Java Kernel will continue installing bundles until the entire JRE is installed.

For more information, see the Java Kernel FAQ.  Also, you can obtain the latest release of Java SE 6 Update N from this page. Of course, if you download and install Java SE 6 Runtime Update N build 10, you won't see the Java Kernel in action, because its purpose is to download the JRE.  If you want to observe the Java Kernel in action, just download the Java SE 6 Runtime (JRE) Update N build 10 Kernel Installer from that page (after having removed Java SE 6 Update N if it was already on your machine).  The FAQ referenced above explains how to tell when the Java Kernel has not yet finished downloading the complete JRE.

So here's the really neat part for compiled JavaFX Script: The plan is to make bundles out of the JavaFX platform jar files, as well as to leverage the automatic update facility of Java SE 6 Update N to keep the JavaFX jar files current.  As Chris Oliver puts it, "we have to be competitive with the Flash experience".

Here are a couple of other interesting JavaFX Script-related things to check out:

Compiled JavaFX Script and Java SE 6 Update N are quickly coming together to the point where we'll soon have fast deploying and executing JavaFX Script RIAs (rich internet applications) that are simple, elegant, and leverage the power of Java.  The next few months are going to be exciting, and in my opinion, will revolutionize (and greatly simplify) the way we develop internet applications.

JavaFX Script Boot Camp Announcement

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!

More details to follow,
Jim Weaver
JavaFX Script: Dynamic Java Scripting for Rich Internet/Client-side Applications
Immediate eBook (PDF) download available at the book's Apress site

January 15, 2008

Turn the Page: Creating a Compiled JavaFX Script BookPanel

I'd like to show you a novel (pardon the pun) and useful JavaFX Script widget called a BookPanel.  It's perfect for enabling the user to view content using a book metaphor.  To turn pages, you can either press the buttons below the pages, or as shown in the screenshot below you can drag the mouse to turn the pages:

Compiledbookpanelexample_3

Please take a look at the compiled JavaFX Script program that produced this output:

/*
*  CompiledBookPanelExample.fx
*
*  Developed 2007 by James L. Weaver (jim.weaver at lat-inc.com)
*  to serve as a compiled JavaFX Script example.
*/

import javafx.ui.*;
import javafx.ui.canvas.*;
import java.awt.Rectangle;
import java.lang.System;

Frame {
  var textFont =
    Font {
      face: FontFace.COMIC_SANS_MS
      style: FontStyle.BOLD
      size: 20
    }
  title: "Compiled BookPanel Example"
  width: 700
  height: 350
  background: Color.WHITE
  visible: true
  onClose:
    function() {
      System.exit(0);
    }
  content:
    BorderPanel {
      var bookPanel =
        BookPanel {
          pageBounds: new Rectangle (0, 0, 350, 300)
          leftPageIndex: 0