Click here to Skip to main content
15,885,366 members
Articles / Web Development / ASP.NET
Article

Intro to JavaFX - Dummy Chess

Rate me:
Please Sign up or sign in to vote.
4.80/5 (28 votes)
17 Apr 2009CPOL30 min read 139.7K   4.1K   54   18
This article describes my experiences in trying to learn the JavaFX programming language by writing a Chess program.

Screen shot of Chess JavaFX Program

Sample screen shot of the Chess program written in JavaFX

Introduction

Sun recently released a new programming language for the Java platform called JavaFX. It's primary purpose is to make it easier to develop rich internet applications (RIAs) that can run on a variety of devices including PCs, mobile phones, and Blu-ray players. It is most often compared to the new RIA languages from Microsoft (Silverlight) and Adobe (AIR). JavaFX is not limited to creating RIAs. In this article, I develop a Chess application that runs on the desktop. I call it dummy chess because the algorithm I used just selects a move at random. So if you can't beat this chess program, then you are a worse player than me. And that's pretty bad. Perhaps one day I'll write a better chess playing program and post an article called smart chess.

Background

I have been programming in Java for over 5 years and recently decided to teach myself JavaFX. I figured the best way to learn JavaFX would be to assign myself a programming project on a topic I'm interested in and to complete the project in the new language. So over the course of about a month I wrote a Chess program written in JavaFX. This article describes my learning experience and introduces the basics of programming in JavaFX.

I was expecting JavaFX to be just like Java only with a new set of framework classes to learn. I was wrong. JavaFX is an entirely different programming language. In order to help the experienced Java programmers who are reading this article, I have put in some code translations from JavaFX to Java to help you better understand the sample JavaFX code posted in this article.

Using the Code

I use IDEs at work (Eclipse and Visual Studio) and really enjoy working with them. For this project, however, I decided not to use an IDE. I like working on my VI skills whenever I can. The following list describes the downloads that you will need depending on whether you are using the command line or an IDE:

  • In order to build the Chess program from the command line, you must download the JavaFX 1.1 SDK.
  • If you prefer to work in an IDE, you can download the NetBeans IDE 6.5 for JavaFX 1.1.
  • If you prefer Eclipse to NetBeans, there is a plugin available.
  • In order to convert SVG to JavaFX graphics, you also need to download the JavaFX 1.1 Production Suite.

All of these downloads are available from javafx.com except for the Eclipse plugin which is available from kenai.com. I have never used NetBeans or Eclipse to build a JavaFX program, so I am not sure how good either of them works.

The source code files contain both Java 1.6 and JavaFX 1.1 source code files. So you will also need a Java 1.6 compiler to build the code. NetBeans and Eclipse will already come with the Java compiler, if you are building from the command line you will need to download the latest JDK from java.sun.com.

In order to build the source code from the command line and run the Chess program, enter the following from the command line (do this from the directory that contains the source code):

Compiling and Running the Chess Program

javac *.java
javafxc *.fx
javafx Main

javac is the command line compiler that compiles the Java source code files, javafxc is the command line compiler that compiles the JavaFX source code files, and javafx runs the Chess program by running the specified JavaFX file (in this case it starts running the code compiled from the file Main.fx).

Points of Interest

JavaFX is a very young programming language and it definitely felt like a beta product. I've never found so many bugs in a programming language before, and I only used it for a month writing one application. I found workarounds for some of the bugs but not all of them. I never was able to change the icon for the application.

The language does have a lot going for it and hopefully these bugs will get resolved in time as the product matures. I really like the binding feature and found creating a GUI (Graphical User Interface) to be much easier compared to when I was first learning how to make GUIs in Java. Java is a very mature language (it's been around for over a decade) and a lot of code has been written for Java. It is nice to be able to instantiate this large library of existing code Java directly from JavaFX code.

If you do decide to take the plunge and do some JavaFX programming, be aware that a lot of the sample JavaFX code that I found on Google does not work in the latest version of JavaFX (v. 1.1). JavaFX must have changed pretty significantly since around mid 2008. I found that most articles published prior to mid 2008 did not work in version 1.1, while most code published in late 2008 and 2009 seems to work just fine.

Let's Go!

Let's start with a simple Hello World program and build our chess program from there. As you'll soon see, you actually learn quite a bit from the Hello World program. Here is the code for Hello World in JavaFX:

Hello World Program Written in JavaFX

Java
println("Hello World!");

That's it! One line of code. This simple one line program has a lot to teach us. The equivalent program in Java is:

Hello World Program Written in Java

Java
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

No Main or Class Needed

So what have we learned from this? Well if you are coming from a Java background, you are used to putting everything in classes. That's because in Java, no code can exist outside of a class. As you can see, this is not the case in JavaFX.

Further, in Java programs you must create a main method that is declared public static void that takes a single parameter String[] args (the command line arguments). In JavaFX this is unnecessary. You simply place instructions in a JavaFX script file and run that file. No main necessary.

If you need to access command line arguments, then you can do so using the special function called run. run is a special function that serves as a script's main entry point. The run function looks like this:

"run" is an Optional Function that Acts as a JavaFX Script's Main Entry Point

Java
function run(args: String[]) {
    println("{args[0]}");
}

This program simply prints out the first command line argument. Surprisingly, it runs without error if no command line arguments are passed. It simply prints out a new line if no command line args are passed in.

Sequences vs Arrays

Let's talk about this run function a little more. It takes a single parameter that is a sequence of String objects called args. Sequences in JavaFX look like arrays in Java. They are declared by placing brackets, [], after the type. For example String[] declares a sequence of Strings. Sequences are immutable, just like arrays. However, you can write code to insert elements into a sequence and remove elements from a sequence like this:

You Can Add to and Remove from Sequences

Java
var colors = ["Red"];
insert "Blue" into colors;
insert "Green" after colors[1];
insert "White" before colors[0];
delete "White" from colors;
delete colors[0];
println("{sizeof colors}");

Since sequences are immutable, Javafx ends up creating a new sequence when items are inserted into or removed from a sequence. This is similar to how Strings work in Java. You can modify a String in code, but since Strings are immutable the runtime ends up creating a new String for you if you modify a String.

JavaFX has some neat ways of creating numeric sequences using the special .. code. The following code gives several examples of building numeric sequences:

Examples of Creating Numeric Sequences in JavaFX

Java
def seq1 = [1..10];  // seq1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def seq2 = [1..10 step 2];  // seq2 = [1, 3, 5, 7, 9]
def seq3 = [10..1 step -1];  // seq3 = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

The for loop in JavaFX only works on sequences. It has the form for(varName in sequence) { ... }. The following code shows how to convert from a simple Java for loop to a JavaFX loop:

Differences Between Java and JavaFX "for" Loops

Java
// Java for loop
for(int i=0; i<10; i++) {
    System.out.println("Hello World!");
}

// equivalent JavaFX for loop
for(i in [0..9]) {
    println("Hello World!");
}

In JavaFX, you can use the indexof keyword to get the index number of the item in the sequence. For example:

Looping Through a Numeric Sequence and Using the "indexof" keyword

Java
for(i in [100..110 step2]) {
    println("{indexof i} = {i}");
}

// Output
0 = 100
1 = 102
2 = 104
3 = 106
4 = 108
5 = 110

Variables and Parameters

You have now seen two different variable/parameter declarations in JavaFX code. The first was the args parameter in the run function (declared as type String[]) and the second was the colors variable in the sequence example (no type declaration). All variables and parameters must be declared in JavaFX, the type however is optional. If you don't declare the type of the variable, the runtime figures it out based on the type of object assigned to the variable. I personally prefer giving my variables a type and you can do this if you like by placing a : after the name followed by the type. For example we can assign a type to our colors variable like this:

Declaring a Variables Type is Optional

Java
var colors: String[] = ["Red"];

Function parameters are simply declared with a variable name and an optional type. Variables are declared as either var or def. The difference between the two is that a var can be modified while a def cannot. A variable declared as a def is similar to a variable declared as final in Java. So the following is valid code:

Variables Defined as "def" Can be Modified

Java
// valid code
var i = 7;
i = 2;

While the following will not compile:

Variables Defined as "def" Cannot be Modified

Java
// invalid code, won't compile
def i = 7;
i = 2;

Function parameters cannot contain the var or def keywords. All function parameters are considered to be defs and thus cannot be modified.

Functions

All functions must declare that they are a function by using the keyword function. I found this requirement to be quite surprising. Scripting languages usually are known for their conciseness. This keyword seems unnecessary and it's a big word too, a whole 8 characters. It seems like the compiler should be able to tell a function when it sees it.

Every function has a return type. If you don't declare a return type, it defaults to Void. That is not a typo, in Java void is spelled with a lower case 'v', while in Javafx it is spelled Void with a capital 'V'.

Function return types are placed similarly to types in variables and parameters. We declare the return type by placing a : after the closing parenthesis followed by the return type. For example the following function returns an Integer:

A "function" in JavaFX that Returns an "Integer"

Java
function foo(i: Integer): Integer {
    i * 4;
}

Built-in Data Types

As you saw in the last code example, the keyword Integer represents an integer. In Java this is equivalent to an int. If you need a decimal, you can use the built in type Number which represents a floating point number. Usually these will be the two numeric types that you will use, but if you need variables of a particular size you can use any of the following built in numeric types: Byte, Short, Number, Integer, Long, Float, Double, and Character (all capitalized in the first letter).

Other built in data types are Boolean which is just like bool in Java, Duration which is a duration of time, and String. When assigning to a Duration type, you basically give the amount of time followed by the time units. For example:

Different "durations" in JavaFX

Java
100ms;  // equal to 100 milliseconds
200s;   // 200 seconds
300m;   // 300 minutes
400h;   // 400 hours

Strings work similarly in JavaFX and Java. JavaFX String objects have the same methods as Java String objects but concatenating Strings works differently. Here are some examples that show String operations in Java followed by the equivalent operations in JavaFX:

"String" Concatenations in Both Java and JavaFX

Java
// Java code
String strA = "A";
String strB = "B";
String strC = strA + strB;
String strD = strC.replaceAll("A", "C");

// equivalent JavaFX code
var strA: String = "A";
var strB: String = "B";
var strC: String = "{strA}{strB}";
var strD: String = strC.replaceAll("A", "C");

Expressions

This is the second time we have seen the user of {} inside of a String. When placed inside of a string {} converts the enclosed expression into a String. We saw this in the run function where we printed out the first command line argument with the code println("{args[0]}"). We also use this for concatenating Strings as in the previous code example. We can place any expression inside of the {}. For example, the following code prints The value of foo(7)is 49:

Converting Expressions to "Strings" in JavaFX

Java
function foo(i: Integer): Integer {
    i * i;
}

def i = 7;
println("The value of foo({i}) is {foo(i)}");

The {i} converts the value of i to a String while {foo(i)} converts the result of foo(i) to a String. These are concatenated with the Strings "The value of foo(", ") is ", and ")". The equivalent Java code would be:

Converting Expressions to "Strings" in Java

Java
int foo(int i) {
    return i * i;
}

int i = 7;
System.out.println("The value of foo(" + i + ") is " + Integer.toString(foo(i)));

Return the Optional Keyword

In case you haven't noticed, I have written two foo() functions that each return Integers. However, neither function used the return keyword. The return keyword is optional in JavaFX. If you do not include the return keyword, the value of the last executed expression is returned. If you prefer, you can use the return keyword as in the following example:

You Can Still Use the "return" Keyword If You Want

Java
function foo(i: Integer): Integer {
    return i * i;
}

def i = 7;
println("The value of foo({i}) is {foo(i)}");

Graphical User Interface

Enough basics, let's start making our chess program. The first thing we will need to do is create a window for our application. In Java you do this with the JFrame class. The equivalent class in JavaFX is Stage. The following shows how to create a window in both Java and JavaFX:

Creating Windows in Java and JavaFX

Java
// Java code
import java.io.*;
import javax.swing.*;

public class Main {
    public static void main(String[] args) {
        JFrame frame = new JFrame("Chess");
        frame.setBounds(100, 100, 400, 400);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        String workingDir = System.getProperty("user.dir");
        String iconFilename =
		workingDir + File.separator + "res" + File.separator + "Icon32.png";
        ImageIcon icon = new ImageIcon(iconFilename);
        frame.setIconImage(icon.getImage());
        frame.setVisible(true);
    }
}

// JavaFX code
import java.io.*;
import java.lang.*
import javafx.scene.image.*;
import javafx.stage.*;

def stage: Stage = Stage {
    title: "Chess"
    x: 100, y: 100, width: 100, height: 100
    icons: [ Image {url: "{__DIR__}res{File.separator}Icon32.png" } ]
    onClose: function() {
        System.exit(0);
    }
}

JavaFX is Buggy

Here are screenshots of each of the two applications. The Java application is the one with the custom chess icon, while the JavaFX application has the default Java application icon. This is the first of several bugs that I found in JavaFX. I did some Google searching and it looks like you cannot load a custom application icon for your program in JavaFX. Here are the forums that discuss the bug, bug1 and bug2. According to the JavaFX 1.1 docs you should be able to set the icon for a Stage. Unfortunately it doesn't seem to work. So we will have to do with the default Java icon for now. Hopefully this issue will get resolved soon.

JavaFX Version 1.1 is Still Buggy, You Cannot Change the Icon for the Main Window

Screen shot of JavaFX and Java Windows, JavaFX cannot load a custom icon.

Creating Objects

In Java, new classes are created with the new keyword. In JavaFX, you don't have to use the new keyword. You typically create a new object by assigning a variable to the name of the class and then assigning initial values for the class attributes inside of braces ({}). You set the initial name of a class attribute by giving the attribute name, followed by a : followed by the initial value for that class attribute. You can place commas between each assignment of values to class attributes, but this is optional. In the example above, I only used commas between the attributes that I assigned on the same line (the x, y, width, and height attributes). The icons attribute is a sequence so I place the icon for the Stage inside brackets, []. The icon is an instance of an Image object. So I create an instance of this class similarly by initializing the class attributes inside of curly braces. In this case, I set the url for the location of the image to use for the icon. The onClose attribute is initialized to point to a function. The function simply calls System.exit(0).

You can still use the new keyword to instantiate classes if you like. For example here is a JavaFX program that works identically to the previous example, but uses new to create the Stage. Classes in JavaFX have no constructors. Since Stage is a JavaFX class, we cannot assign initial values to the object by passing values to the constructor (there is no constructor). So we assign values to the class attributes after creating the new object as shown in the code below:

You Can Still Use the "new" Keyword to Create JavaFX Objects, But I Don't Like it As Much

Java
import java.io.*;
import java.lang.*;
import javafx.scene.image.*;
import javafx.stage.*;

def stage: Stage = new Stage();
stage.title = "Chess";
stage.x = 100;
stage.y = 100;
stage.width = 400;
stage.height = 400;
stage.icons = [ Image { url: "{__DIR__}res{File.separator}Icon32.png" } ];
stage.onClose = function() {
    System.exit(0);
};

When creating Java objects, it makes sense to create them with the new keyword since Java objects have constructors and you can then pass parameters to these constructors by using the new keyword. When creating JavaFX objects, I prefer to NOT use the new keyword. I think the code looks a lot cleaner when I create objects without it and instantiate all of the class attributes using the enclosing curly braces, {}, as shown in the first example.

Saving Application Settings

I always like saving applications settings in the user's home directory. In the case of GUI applications, the least that I will do is to save the location and size of the applications main window. Java has been around for a long time and already has a set of classes to help us accomplish this task. Let's first see how to do this in Java and then we will do the same thing in JavaFX.

Saving Application Settings in Java

Java
// Java code
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import javax.swing.*;

public class Main {
    public static void main(String[] args) {
        // get the users home directory
        String homeDir = System.getProperty("user.home");

        // these are declared final so they can be accessed in the
        // anonymous inner class below
        final String settingsFilename =
		homeDir + File.separator + "mySettings.properties";
        final Properties props = new Properties():

        // Load the saved settings
        try {
            FileInputStream input = new FileInputStream(settingsFilename);
            props.load(input);
            input.close();
        } catch(Exception ignore) {
            // we ignore the exception since we expect the
	   // settings file to not exist sometimes
            // on the first run of the application it will definitely not exist
        }

        int savedX;
        try {
            savedX = Integer.parseInt(props.getProperty("xPos", "100"));
        } catch(NumberFormatException e) {
            savedX = 100;
        }

        // similar code to load savedY, savedWidth, savedHeight left out for brevity

        // Create and show the window, same code as before except on close we do nothing
        // We also make the JFrame final so it can be accessed
        // in the anonymous inner class
        final JFrame frame = new JFrame("Chess");
        frame.setBounds(savedX, savedY, savedWidth, savedHeight);
        // different from previous example
        frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        String workingDir = System.getProperty("user.dir");
        String iconFilename = workingDir + File.separator +
			"res" + File.separator + "Icon32.png";
        ImageIcon icon = new ImageIcon(iconFilename);
        frame.setIconImage(icon.getImage());
        frame.setVisible(true);

        frame.addWindowListener(new WindowAdapter() {
            @Override public void windowClosing(WindowEvent e) {
                // Save settings on exit
                Rectangle frameBounds = frame.getBounds();
                props.setProperty("xPos", frameBounds.x);
                props.setProperty("yPos", frameBounds.y);
                props.setProperty("width", frameBounds.width);
                props.setProperty("height", frameBounds.height);

                try {
                    FileOutputStream output = new FileOutputStream(settingsFile);
                    props.store(output, "Saved settings");
                    output.close();
                } catch(Exception ignore) {
                    // if the settings can't be saved we'll
	           // just use the defaults next time
                }

                // exit the application
                System.exit(0);
            }
        });
    }
}

Properties

We use the Properties Java class to save the applications settings. This class contains a dictionary of key/value pairs. We use the load method to load the saved properties and the store method to save the properties. We call the getProperty method to get the value for the specified key. We also pass in a default value parameter, this value is returned if the key is not set in the Properties object. We save properties using the setProperty method. This is one of several ways of saving application settings in Java.

WindowListener

We load the application settings before we display the window so that we can set the saved position and location of the window. We save the application settings by adding a WindowListener to the JFrame. Our WindowListener will be notified when the user tries to close the application window. When this occurs, we store the size and location of the JFrame in our Properties object and save it to the user's home directory. We then exit the application by calling System.exit(0).

Saving Settings in JavaFX

We can use the same classes to save application settings in our JavaFX class. The only difference is we do not need to add a WindowListener, we simply have to update the onClose function of our Stage class to save the application settings before exiting the application. This is shown in the code below:

Saving Application Settings in JavaFX

Java
import java.io.*;
import java.lang.*;
import java.util.*;
import javafx.scene.image.*;
import javafx.stage.*;

// find the settings file in the users home directory
def homeDir = System.getProperty("user.home");
def settingsFile = "{homeDir}{File.separator}"mySettings.properties";

// load the settings into the Properties class
def props: Properties = new Properties();
try {
    def input: FileInputStream = new FileInputStream(settingsFile);
    props.load(input);
    input.close();
} catch(ignore: Exception) {
    // if the file doesn't exist, that's OK we will just use the default values
}

// read the saved settings
var savedX: Number;
try {
    savedX = Double.parseDouble(props.getProperty("xPos", "100"));
} catch(e: NumberFormatException) {
    savedX = 100;
}
// similarly load savedY, savedWidth, and savedHeight settings

// create our window as before, only add saveSettings() call to onClose function
def stage: Stage = Stage {
    title: "Chess"
    x: savedX, y: savedY, width: savedWidth, height: savedHeight
    icons: [ Image {url: "{__DIR__}res{File.separator}Icon32.png" } ]
    onClose: function() {
        saveSettings();  // save settings before exiting
        System.exit(0);
    }
}

function saveSettings(): Void {
    props.setProperty("xPos", "{stage.x}");
    props.setProperty("yPos", "{stage.y}");
    props.setProperty("width", "{stage.width}");
    props.setProperty("height", "{stage.height}");

    try {
        def output: FileOutputStream = new FileOutputStream(settingsFile);
        props.store(output, "Saved Chess Settings");
        output.close();
    } catch(ignore: Exception) {
        // if the settings can't be saved we'll just use the defaults next time
    }
}

As you can see, we are able to use the same Java classes to save our application settings in JavaFX as we use in our Java apps. This is one of the best features of JavaFX, I can use the large library of existing Java classes in my JavaFX programs. My experience working with the Java Framework is thus applicable to my JavaFX programming.

One thing to notice is that the x, y, width, and height attributes of the Stage class are all defined as Numbers. Thus, when I save them to disk, they are all saved with decimal points. This seems odd to me since the size and position of the Stage object is specified in number of pixels. It doesn't make sense to use a decimal since you can only specify whole pixels, 1.5 pixels makes no sense, it is either 1 or 2 pixels. I'm not sure if this is a bug or not. I've already submitted two bug reports to JavaFX. I'm not sure if another bug report is warranted here or not. It just seems like a weird decision to use Number instead of Integer.

The Chess Program

Now that we have our window up, the next step is to make a chess board. I will be creating a custom chess board GUI component in JavaFX. If I were doing this in Java, my chess board would extend JPanel and I would add this JPanel to my JFrame. In JavaFX, my chess board will extend Scene and will be added to my Stage object.

Before I create my Board extends Scene class, let me explain a little about the board I want to create. In building a JavaFX program, I want to take advantage of the graphical powers of this new language. In this case, I want to use SVG graphics for my chess pieces so they look good in any resolution. This means my chess board is going to have to be resizable by the user, and it has to look good regardless of the bounds of the application's main window. The following screen shot should explain how I want the board to look when I resize the main window.

The Chess Board Resizes and Repositions Itself Based on the Size of the Main Window

Resizeable Chess Board.

The board tries to take up as much of the window as possible while remaining a square. If the board is wider than it is tall, or taller than it is wide, then the board is centered in the window. If the window is square, the board will not take up the entire window but will take up most of the window. It will always leave a small border around the board. The width and height of this border is at least the size of a single square on the chess board. So our board has the following 3 attributes:

  • squareSize - The size of a single square on the chess board
  • xOffset - If the window is wider than it is tall, this tells us how far we have to move the board to make it centered in the window
  • yOffset - If the window is taller than it is wide, this tells us how far we have to move the board to make it centered in the window.

JavaFX Bindings

Binding is a new feature in JavaFX that allows you to bind the value of one variable to another variable or to bind the value of a method to another variable. Binding is performed using the bind keyword. Since the value of the squareSize, xOffset, and yOffset attributes depend on the size of the chess board, we will bind these values to the size of the chess board. Here is the code for our chess board:

Our Chess Board uses "bind" to Dynamically Resize and Reposition Itself

Java
import javafx.scene.*;
import javafx.scene.paint.*;
import javafx.scene.shape.*;

public class Board extends Scene {
    def LIGHT_COLOR: Color = Color.web("lemonchiffon");
    def DARK_COLOR: Color = Color.web("brown");

    public-read var squareSize = bind {
        if(width > height) {
            height / 10;
        } else {
            width / 10;
        }
    }

    public-read var xOffset = bind {
        if (width > height) {
            (width - height) / 2;
        } else }
            0;
        }
    }

    public-read var yOffset = bind {
        if (width > height) {
            0;
        } else {
            (height - width) / 2;
        }
    }

    def board = [ Coord.A8, Coord.B8, Coord.C8, Coord.D8,
			Coord.E8, Coord.F8, Coord.G8, Coord.H8,
                  Coord.A7, Coord.B7, Coord.C7, Coord.D7,
			Coord.E7, Coord.F7, Coord.G7, Coord.H7,
                  Coord.A6, Coord.B6, Coord.C6, Coord.D6,
			Coord.E6, Coord.F6, Coord.G6, Coord.H6,
                  Coord.A5, Coord.B5, Coord.C5, Coord.D5,
			Coord.E5, Coord.F5, Coord.G5, Coord.H5,
                  Coord.A4, Coord.B4, Coord.C4, Coord.D4,
			Coord.E4, Coord.F4, Coord.G4, Coord.H4,
                  Coord.A3, Coord.B3, Coord.C3, Coord.D3,
			Coord.E3, Coord.F3, Coord.G3, Coord.H3,
                  Coord.A2, Coord.B2, Coord.C2, Coord.D2,
			Coord.E2, Coord.F2, Coord.G2, Coord.H2,
                  Coord.A1, Coord.B1, Coord.C1, Coord.D1,
			Coord.E1, Coord.F1, Coord.G1, Coord.H1 ];

    postinit {
        for (square in board) {
            def i: Integer = indexof square;
            insert Rectangle {
                fill: if (square.getIsWhite()) LIGHT_COLOR else DARK_COLOR
                x: bind xOffset + ((i mod 8) + 1) * squareSize
                y: bind yOffset + ((i / 8) + 1) * squareSize
                width: bind squareSize
                height: bind squareSize
            } into content;
        }
    }
}

Coord is simply an enum that makes it easier for me to place pieces on the board. Coord also keeps track of whether or not the square at that coordinate is a dark or light colored square. In chess, the queen is always placed on her color and the lower left square on the board is always dark. This helps you keep from misplacing your king and queen on the board. JavaFX cannot create enums, so the Coord class is written in Java. This is why you need both a Java and a JavaFX compiler to build this project. You must build the Coord.java file using the Java compiler before you can build the JavaFX files using the JavaFX compiler.

This class uses a lot of binding. The first bind we see is for the class attribute squareSize. squareSize in this case is bound to an expression. If the value of the expression changes, the value of the squareSize attribute will automatically change to equal the new value of the expression. The expression that this attribute is bound to is a code block. The value of this code block is equal to the last line executed in the code block. In this case, the last line executed depends on the values of the width and height attributes of the Scene. The value of squareSize will be set to either height / 10; or width / 10;.

Our Board class will be placed in our Stage class (the main window for our application). Our Board will fill the entire client area of the Stage, this is basically the entire window excluding the frame. If we change the size of the window, the width and height attributes of our Board class will be modified. As these values are changed, the JavaFX runtime will automatically adjust the value of the squareSize attribute for us.

Similarly, the xOffset and yOffset attributes are also bound to the values of width and height.

postinit Instead of Constructors

The next set of binds that we see are in the postinit code block. Like I said earlier, JavaFX classes do not contain constructors. You often need to have code executed when an object is first instantiated. JavaFX allows you to specify code to be executed on object instantiation through the use of the postinit keyword. Code placed within the postinit code block will automatically be executed on the instantiation of the object, similar to a constructor call in Java.

In the Boards postinit block, we create a Rectangle for each square on the chess board. The location and size of each of the Rectangles on the board is bound to the values of the squareSize, xOffset, and yOffset. This is done by placing the bind keyword after the x, y, width, and height attributes of each of the Rectangles used for each of the squares and placing the expression that the attribute should be bound to after the bind keyword.

JavaFX has a New Set of Access Modifiers

The Board class uses an access modifier that does not exist in the Java programming language, public-read. JavaFX uses a new set of access modifiers which I will describe right now:

  • default - If you do not use an access modifier for a variable or function it defaults to having script-only access. This means you can only access the variable/function from within the current script file. JavaFX has no private access modifier, the default access modifier in JavaFX is equivalent to private in Java. (Note: I use the terms variable and function since JavaFX does not require you to use classes, but if you are using a class I guess the better terms would be attribute and method.)
  • package - This makes the variable or function available to any other code in the same package. This is similar to the default access modifier in Java (Java does not use the package keyword as an access modifier.
  • protected - This makes the variable/function available to any other code in the same package and also to all subclasses. Java uses the same keyword and it means the same thing.
  • public - This makes the variable/function available to everybody for both read and write access. Java uses the same keyword and it means the same thing.
  • public-read - We used this access modifier in our Board class. This access modifier can only be applied to variables, it cannot be applied to functions.This gives everyone read access to the variable while restricting write access to the script. Java does not have a comparable access modifier. You can get the same functionality in Java by declaring the attribute private and providing a public method that retrieves the value of the attribute. An example of this is given in the code below.
  • public-init - A variable marked as public-init can be written to by anyone when the object is first created. After the object is created, it can be read by anyone but can only be written to by the script that defines the variable. Like the public-read access modifier, this public-init access modifier can only be used on a variable, not on a function. An equivalent access modifier does not exist in Java. You can get the same functionality in Java by declaring the attribute private, providing a public method that retrieves the value of the attribute, and allowing the attributes initial value to be set through a parameter to the constructor. An example of this is given in the code below:

"public-read" and "public-init" JavaFX Code Translated to Java Code

Java
// JavaFX code
public-read size: Integer;
public-init height: Integer;

// Similar code in Java
public class MyClass {
    // only this class can write to the attribute size
    private int size;
    private int height;

    // other classes can only write to height through this constructor
    // after the object is created only this class can write to height
    public MyClass(int initHeight) {
        height = initHeight;
    }

    // everybody can read the value of size
	public int getSize() {
        return size;
    }

    // everybody can read the value of height
    public int getHeight() {
        return height;
    }
}

There is one thing that I can do in Java which I was unable to do in JavaFX. In Java, I will often have a class attribute declared as final, I can do this in JavaFX using the def keyword. However, in Java I can give anyone permission to set this attribute's initial value by passing in a value through the constructor. In JavaFX, you cannot do this. I tried declaring a variable as public-init def, but this produces an error if I don't initialize the variable right there.

In JavaFX, You Cannot Initialize a "def" (i.e. final) Variable from Another Class

Java
// the following is valid Java code for which there
// seems to be no comparable code in JavaFX
public class Test {
    public final int i;

    public Test(int initI) {
        i = initI;
    }
}

// the following JavaFX code will not compile
public class Test {
    public-init def i: Integer;
}

def test: Test = Test { i: 7 }

// here is the error message
Test.fx:2: The 'def' of 'i' must be initialized with a value here.
				Perhaps you meant to use 'var'?
    public-init def i: Integer

1 error

// if I initialize the attribute i, I get more error messages
class Test {
    public-init def i: Integer = 4;
}

def test: Test = Test { i: 7 }

// here are the error messages
Test.fx:2: modifier public-init not allowed on a def
    public-init def i: Integer = 4;

Test.fx:5: You cannot change the value(s) of 'i'
	because it was declared as a 'def', perhaps it should be a 'var'?
    def test: Test = Test { i: 7 }

2 errors

It seems useful to be able to have a variable declared as public-init def. I wanted to use this for my chess pieces by initializing a piece as either black or white when I create the piece. Once a piece's color is set, it can never be changed so I wanted to declare it as a def but was unable to do so. I had to declare it a public-init var and had to make sure and not change it after it is first initialized. This is the second design decision made in JavaFX which just seems wrong to me. (The first being the decision to declare the attributes x, y, width, and height of the class Stage as Numbers instead of Integers.)

SVG Chess Pieces

The chess pieces that I use in this chess program come from openclipart.org. This website has a good selection of open source SVG graphics that you can use in your applications. Many thanks to them for supplying me with pretty graphics to use in my program since I am not much of an artist. I was originally under the impression that JavaFX could directly load SVG graphics. This is not the case. In order to use the graphics, I had to convert them from SVG to the JavaFX graphics format. In order to convert SVG to JavaFX graphics, you need to download the JavaFX 1.1 Production Suite from javafx.com. You use the "SVG to JavaFX Graphics Converter" program that comes with this suite of tools to convert the SVG graphics to JavaFX graphics. Here is a screen shot of the chess pieces used in this program:

The SVG Chess Piece Graphics Used

Screen shot of SVG Chess Pieces.

Loading and Displaying the Chess Pieces

I came across another JavaFX bug when trying to load my JavaFX graphics formatted chess pieces. According to the documentation, I should be able to load the pieces with the following code:

This Code Should Load JavaFX Graphics Files, But It Does Not Work

Java
// load the JavaFX graphics file
var pieceNode = FXDLoader.load("{__DIR__}res/WKing.fxz");
// add the graphic to a Group object
var group = Group {
    content: [
        pieceNode
    ]
}
// add the graphic to the board
insert group into board.content

The code listed above does not work. __DIR__ is a special constant that returns the URL of the directory that contains the current JavaFX source file. In all of the sample code that I saw, all resources (graphics, icons, etc.) for JavaFX programs are accessed relative to __DIR__. In most cases, this works. For example, to load an image you can enter code like the following:

Loading an Image in JavaFX

Java
var myImage = Image { url: "{__DIR__}images/SampleImage.jpg" backgroundLoading: true};

However if you try to load an image using the FXDLoader class using this method, it will fail. The problem is the value returned from __DIR__ is a URL. This means spaces are converted to %20. FXDLoader fails with a file not found message. The fix for the problem is to convert the %20s back to spaces as in the following code:

Fix to Load the JavaFX Graphics

Java
// bug fix, convert %20 to space so FXDLoader can load the graphics
def rootDir = "{__DIR__}".replaceAll("%20", " ");

// load the JavaFX graphics file
var pieceNode = FXDLoader.load("{rootDir}res/WKing.fxz");
// add the graphic to a Group object
var group = Group {
    content: [
        pieceNode
    ]
}
// add the graphic to the board
insert group into board.content

So I submitted another bug report saying that FXDLoader needs to be updated to handle URLs just as in the case of the class Image. Sun's sample code recommends using __DIR__ to load other resources. It should work the same with JavaFX Graphics objects using the FXDLoader class.

Custom Mouse Cursor in JavaFX

Better get this wrapped up soon, this article is getting kinda long. The last thing I did which might be useful to JavaFX programmers is load a custom mouse cursor. I wanted my chess program to highlight the piece that the mouse is over and to change the mouse cursor to the open hand shown in the figure below and to change to the closed hand when the piece is clicked and dragged. I also highlight the square over which the piece will be placed if it drops the piece. This is useful in cases where the user hovers over a square corner you might not know which square the piece will be dropped in without this UI hint.

Highlighted Pieces and Custom Cursors on Mouse Over and Mouse Drag

Screen shot highlighted pieces and custom cursors on mouse over and mouse drag.

JavaFX has its own set of default cursors available for use. If you want to create your own custom cursor, you have to create a new java.awt.Cursor object for your custom cursor as in the following code:

Custom Cursor

Java
import java.awt.*;
import java.net.*;
import javax.swing.*;
import javafx.scene.Cursor;

public class CustomCursor extends Cursor {
    public-init var imageURL: String;
    public-init var cursorName: String;

    public override function impl_getAWTCursor(): java.awt.Cursor {
        var toolkit = Toolkit.getDefaultToolkit();
        var image: Image = toolkit.getImage(new URL(imageURL));
        var point: Point = new Point(16, 16);
        var cursor: java.awt.Cursor =
		toolkit.createCustomCursor(image, point, cursorName);
    }
}

This code is a little confusing because we are using two different Cursors. First, our new class CustomCursor extends the JavaFX Cursor class (javafx.scene.Cursor) class. Second, our class creates a new Java Cursor object (java.awt.Cursor). So these are two different Cursor classes. One thing I noticed is the impl_getAWTCursor() method that I am overriding is not listed in the JavaFX 1.1 API documentation. It could be because the JavaFX API documentation is crap right now (it doesn't feel nearly as complete as the Java API documentation) or maybe this is a hidden API that should not be overridden.

As far as the JavaFX API documentation being worse than the Java API documentation, I guess that's to be expected. Java has been around a lot longer and is a much more mature language. However, there is one thing I really dislike about the JavaFX API docs. In the Java docs, all of the classes are listed in a single HTML pane. So if I am searching for a particular class, I can easily find it in my browser. In JavaFX, if you know the class name that you are looking for but you do not know the package that it is in, it can be quite difficult to find in the documentation. In the JavaFX API docs, they don't list all of the classes in the side pane (as in the Java docs) but they list all of the package names in the side pane. You have to click on a package name to see the classes in that package. So if you want to find the documentation for the Cursor class and you don't know that it is in the javafx.scene package, it can be difficult to find. The docs may look prettier, but more usable they are not.

Back to loading our custom cursor. The following code loads our custom cursor into the chess program:

Loading the Custom Cursor

Java
def rootDir = "{__DIR__}".replaceAll("%20", " ");
def grabCursor = CustomCursor {
    imageURL: "{rootDir}res/grab.png"
    cursorName: "Grab"
}
def grabbingCursor = CustomCursor {
    imageURL: "{rootDir}res/grabbing.png"
    cursorName: "Grabbing"
}

var currentCursor = Cursor.Default;
// Inside of the Piece class we create custom mouse listener
// functions to change the cursor
onMouseEntered: function(e: MouseEvent) {
    currentCursor = grabCursor;
    board.cursor = currentCursor;
}
onMouseExited: function(e: MouseEvent) {
    currentCursor = Cursor.Default;
    board.cursor = currentCursor;
}
// similar for other mouse functions released, pressed, clicked

Bye

Well that's it. I found JavaFX to be a fun language to work with, however it feels buggy. I found some bugs, some design decisions which just seem wrong, the API documentation is not as complete as the documentation for Java, and a lot of the code that exists online was written for an earlier version and does not work with the latest version (1.1) of JavaFX. Given the number of bugs and the fact that the language has changed a lot from earlier versions, you might want to wait until the product matures before you build a major application in JavaFX. However, if you are just writing a simple Chess application (or some other simple program), it is a fun language to work with.

My First Article!

I've been a CodeProject member for almost 9 years now and I finally got around to publishing my first article. I'm hoping to finally graduate (MS in CS) this May. I've been working full time while going to school part time for a long time now. Hopefully after graduating I will have more time to write some more articles on CodeProject. I hope you enjoyed the article and appreciate your votes if you did.

History

  • Version 1.0 - April 16, 2009

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
United States United States
Started coding in 1994 and haven't stopped since.
Received my BS in Computer Science from Trinity University in 1999.
Hope to receive my MS in Computer Science from the University of Texas at San Antonio May of 2009.
Have been working full time as a Software Engineer since 2000 writing code in C, C++, C#, MFC, Java, and Python.

Comments and Discussions

 
QuestionMy vote of 5 Pin
Farhad Reza24-Oct-16 6:27
Farhad Reza24-Oct-16 6:27 
Generalout dated Pin
Aamir Khan7-Nov-15 23:28
Aamir Khan7-Nov-15 23:28 
Buggreatcode Pin
sonofkrypton13-Oct-11 11:42
sonofkrypton13-Oct-11 11:42 
GeneralMy Vote of 5 Pin
RaviRanjanKr22-Mar-11 4:59
professionalRaviRanjanKr22-Mar-11 4:59 
Cool Article.
GeneralGreat Article Pin
Abinash Bishoyi19-Jun-10 11:28
Abinash Bishoyi19-Jun-10 11:28 
Generalthat's cool Pin
Weirdsheyman5-Jan-10 0:13
Weirdsheyman5-Jan-10 0:13 
Generaljavafx dummy chess Pin
rpdcp26-Sep-09 1:04
rpdcp26-Sep-09 1:04 
GeneralRe: javafx dummy chess Pin
Igor Kushnarev27-May-10 1:49
professionalIgor Kushnarev27-May-10 1:49 
GeneralGreat article ! Pin
Member 45802686-Sep-09 19:31
Member 45802686-Sep-09 19:31 
GeneralDodd expands health care bill, cuts costs Pin
wenyue20092-Jul-09 16:56
wenyue20092-Jul-09 16:56 
GeneralMy vote of 2 Pin
Thiru Thirunavukarasu13-May-09 9:38
Thiru Thirunavukarasu13-May-09 9:38 
GeneralNice article - Organize a chess match Pin
Palavos23-Apr-09 21:09
Palavos23-Apr-09 21:09 
GeneralRe: Nice article - Organize a chess match Pin
El Bob-O24-Apr-09 2:55
El Bob-O24-Apr-09 2:55 
GeneralCool article but Pin
Sacha Barber18-Apr-09 1:02
Sacha Barber18-Apr-09 1:02 
GeneralRe: Cool article but Pin
El Bob-O18-Apr-09 5:40
El Bob-O18-Apr-09 5:40 
GeneralRe: Cool article but Pin
Sacha Barber18-Apr-09 21:27
Sacha Barber18-Apr-09 21:27 
GeneralThe article is great, now my question... Pin
thebossedgar17-Apr-09 10:43
thebossedgar17-Apr-09 10:43 
GeneralRe: The article is great, now my question... Pin
El Bob-O17-Apr-09 11:07
El Bob-O17-Apr-09 11:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.