Welcome!

Java Authors: Maureen O'Gara, Bruce Armstrong, Liz McMillan, Walter H. Pinson, III, Yakov Werde

Related Topics: Java

Java: Article

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

This application in JavaFX Script demonstrates several compiled JavaFX Script features

James Weaver’s JavaFX Blog

Kevin Nilson, the leader of the Silicon Valley Web Developer JUG, relayed to me the idea 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:



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}"
                }
            ]
          }
        ]
      };
  }
}

More Stories By James L. Weaver

James L. (Jim) Weaver is founder and president of jMentor, formed in 2000 to provide Java programming-related training to companies and individuals. He has served as a system architect and developer for over 25 years, specializing in leading-edge software development. His specialties include Java, object-oriented, and web-based technologies. He has authored books on the Java programming language, including most recently JavaFX Script, published by Apress.

Comments (2) View Comments

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.


Most Recent Comments
Tattoo Designs 07/24/08 07:03:37 PM EDT

Interesting little idea. I'd like to see it made into a more useful example, or used for a more practical purpose.

MICR Toner 06/23/08 01:51:54 AM EDT

So, this is sort of like a wheel of fortune interface for random selection of a bunch of names we can enter? I'll try it out. It could come in handy one day.