JavaFX Menus

May 13, 2010

I knew those JavaFX menus looked familiar!

adobe_flex

As I was reading this IntelliJ blog article, I noticed what I thought to be a new JavaFX icon (the Fx text on a black gradient background).

Once I started reading the article, I realized this wasn’t about JavaFX at all, but support for Adobe’s Flex! I guess the lower case “x” in “Fx” should have been a give-away that it wasn’t JavaFX, but I wasn’t paying close attention. That made me wonder, is there a potential marketing conflict here? Could this possibly have been an intentional choice on Adobe’s part, or did Sun choose a bad suffix for Java?

I don’t know any of the history on either side, so I can’t really speculate too much. At any rate, I think there’s some potential for confusion in Adobe’s favor.

JavaFX 1.2 arrived silently last night in order to coincide with the opening of JavaOne 2009. I’m happy to see many more UI controls included in this latest release of JavaFX, most notably ScrollBar, which is so painful to re-implement yourself.

Here’s a look at some of the new controls based on a Sun web-start app I found here (run it):
javafx_1_2_ui_controlsHere’s the full list of new UI controls:

  • Button
  • Label
  • CheckBox
  • ToggleButton
  • RadioButton
  • Hyperlink
  • ProgressBar
  • ProgressIndicator
  • Slider
  • ScrollBar
  • ListView
  • TextBox

I wasn’t expecting the controls to have a platform neutral look, though I think this was a good idea (similar to how Adobe Flex controls have an platform agnostic look).

Check out the full JavaFX 1.2 API — note how much fuller it is than in JavaFX 1.1.

I’m interested in hearing you’re thoughts on JavaFX 1.2, specifically about the new UI controls.

Tray component in JavaFX

March 17, 2009

To aid my exploration of JavaFX I created the following tray-type component (as seen in OS X to display Dashboard widgets). When the disclosure button is pressed, the main content area slides up to reveal a tray area where controls or other information can be placed.

The code is straight-forward, containing a DisclosureButton class (the black cross button) and the main script, which composes the tray, main content area and button. I had to inline quite a bit, as many of the components need to reference other components for visual calculations – I’m still learning what the right balance of declarative syntax is.

I’m not particularly happy with the animation performance on OS X (you’ll notice it’s a bit choppy). Commenting out the main content areas drop shadow eliminates this issue – maybe future Java updates on the Mac will address this problem.

Here are the two states of the window:
tray
tray_exposed

You can web start it as well:
launch

And here’s the code (note that I’m not crazy about the way NetBeans formats the code, but I’ve given up fighting with it):

Main.fx

import javafx.scene.effect.DropShadow;
import javafx.scene.Group;
import javafx.scene.paint.Color;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.Scene;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import tray.DisclosureButton;

def trayHeight = 75;

def trayGradient = LinearGradient {
    endX: 0.0
    endY: 1.0
    stops: [
        Stop {
            offset: 0.0
            color: Color.web("#2b2b2b")
        },
        Stop {
            offset: 1.0
            color: Color.web("#1b1b1b")
        } ]
}

def contentGradient = LinearGradient {
    endX: 0.0
    endY: 1.0
    stops: [
        Stop {
            offset: 0.0
            color: Color.web("#7c8697")
        },
        Stop {
            offset: 1.0
            color: Color.web("#596171")
    } ]
}

Stage {
    var scene: Scene
    var rectangle: Rectangle
    var button: DisclosureButton
    var mainContentText: Text

    title: "Tray"
    width: 400
    height: 300
    scene: scene = Scene {
            fill: Color.BLACK
            content: [
                Rectangle {
                    y: bind scene.height - trayHeight
                    width: bind scene.width
                    height: trayHeight
                    fill: trayGradient
                }
                Group {
                    translateY: bind button.buttonPercentRotated * - trayHeight
                    content: [
                        rectangle = Rectangle {
                            width: bind scene.width
                            height: bind scene.height
                            fill: contentGradient
                            effect: DropShadow {
                                color: Color.color(0, 0, 0, 0.35)
                                radius: 10
                                offsetY: 10
                            }
                        },
                        button = DisclosureButton {
                            translateX: 30;
                            translateY: bind scene.height - 30
                        },
                        mainContentText = Text {
                            fill: Color.WHITESMOKE
                            translateX: bind scene.width / 2 - mainContentText.boundsInLocal.width / 2
                            translateY: bind scene.height / 2 - mainContentText.boundsInLocal.height / 2
                            font: Font { size: 20
                            }
                            content: "Your main content here."
                        }
                    ]
                }
            ]
        }
}

DisclosureButton.fx

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.scene.CustomNode;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;

public class DisclosureButton extends CustomNode {

    def startButtonAngle = 0.0;
    def endButtonAngle = -135.0;
    def strokeWidth = 2;
    def buttonRadius = 18;
    def lineRadius = 12;

    public var buttonPercentRotated: Double = 0;

    var buttonRotation: Double = 0 on replace oldValue {
        buttonPercentRotated = buttonRotation / endButtonAngle;
    };


    var buttonPressed = function(Void): Void {
        rotateTimeline.rate = rotateTimeline.rate * - 1;
        rotateTimeline.play();
    }

    def firstKeyFrame = KeyFrame {
        time: 0ms
        values: buttonRotation => startButtonAngle
    }

    def lastKeyFrame = KeyFrame {
        time: 650ms
        values: buttonRotation => endButtonAngle
    }

    def rotateTimeline = Timeline {
        rate: -1;
        keyFrames: [ firstKeyFrame, lastKeyFrame ]
    }

    def buttonBackground = Circle {
        radius: buttonRadius;
        fill: Color.BLACK;
        stroke: Color.WHITE;
        strokeWidth: strokeWidth
        onMouseClicked: buttonPressed
    }

    def lineOne = Line {
        startX: -lineRadius
        endX: lineRadius
        stroke: Color.WHITE
        strokeWidth: strokeWidth;
    }

    def lineTwo = Line {
        startX: -lineRadius
        endX: lineRadius
        rotate: 90
        stroke: Color.WHITE
        strokeWidth: strokeWidth;
    }

    def cross = Group {
        content: [lineOne, lineTwo]
    }

    def button = Group {
        content: [ buttonBackground, cross];
        rotate: bind buttonRotation;

    }

    override public function create(): Node {
        return button;
    }

}

IntelliJ and JavaFX

March 14, 2009

intellijjavafx
If you’re interested in seeing JavaFX support in IntelliJ IDEA (as I am), I encourage you to vote for the feature here. JetBrains has hinted that we’ll see this in IntelliJ IDEA 9, but it can’t hurt to let them know that this is something users really want.

clean_code

I’ve been dabbling with JavaFX script lately and can’t help but notice JavaFX script’s propensity for poor code hygiene. After reading Bob Martin’s Clean Code, I’m hyper aware of code cleanliness. I don’t think there are any fundamental problems with the JavaFX script, only that best practices and good standards have yet to be laid out. I’m sure there was some pretty poor Java code written back in 1995!

Before I go further, let me point out that I think JavaFX script can be just as clean as any other language. I only wish to insight the community to better itself.

Below, I’m going demonstrate how clean JavaFX script can be by transforming some example JavaFX script code that Sun ships with NetBeans into something that better adheres to the principles laid out in Clean Code. You’ll find the original code by downloading NetBeans and selecting File -> New Project, then Samples -> JavaFX -> Building Blocks -> Image -> Background Image. Hopefully the code’s author won’t take offense as none is intended!

In Clean Code, Bob talks about keeping methods small (3 to 4 lines preferably) with a single purpose. Also, he talks about naming variables better in order to eliminate superfluous comments and thus make the code inherently more obvious.

Take a look at the following code – how obvious is this code to you?

// vertical position of moving line
var y : Number;

var timeline = Timeline {
    repeatCount: Timeline.INDEFINITE
    keyFrames : [
        KeyFrame {
            time : 0s
            values :
                y => -20
        },
        KeyFrame {
            time : 3s
            values :
                y => 200 tween Interpolator.LINEAR
        }
    ]
};

// Visual representation
Stage {
    scene : Scene {
        content : [
            // Shows image on the background
            ImageView {
                image : Image { url : "{__DIR__}background.png" }
            },
            // Paints two moving lines
            Group {
                transforms : Translate { y : bind y } // Bind position of lines to y
                content : [
                    Line {
                        startX : 0, startY : 20, endX : 200, endY : 0
                        stroke : Color.RED
                    },
                    Line {
                        startX : 0, startY : 20, endX : 200, endY : 0
                        stroke : Color.RED
                        transforms : Translate { y : 10 }
                    }
                ]
            }
        ]
    }

    width : 200
    height : 232
    title : "Background Image"
}

// Start the line animation
timeline.play();

It’s very easy to let things run-on in JavaFX script. I find it difficult to follow such deeply nested code as its not easy to discern what goes with what.

Here’s my refactored version of the code, which in-lines very little:

def imageUrl = "{__DIR__}background.png";
def backgroundImage = Image { url: "{__DIR__}background.png" };
def backgroundImageView = ImageView { image: backgroundImage };

var verticalPositionOfLine: Number;

def firstKeyFrame = KeyFrame {
    time: 0s
    values: verticalPositionOfLine => -20
}

def secondKeyFrame = KeyFrame {
    time: 3s
    values: verticalPositionOfLine => 200 tween Interpolator.LINEAR
}

def timeline = Timeline {
    repeatCount: Timeline.INDEFINITE
    keyFrames: [ firstKeyFrame, secondKeyFrame ]
}

def lineOne = Line {
    startX: 0,
    startY: 20,
    endX: 200,
    endY: 0
    stroke: Color.RED
}

def lineTwo = Line {
    startX: 0,
    startY: 20,
    endX: 200, 
    endY: 0
    stroke: Color.RED
    transforms: Translate { y: 10 }
}

def lineGroup = Group {
    transforms: Translate { y: bind verticalPositionOfLine }
    content: [ lineOne, lineTwo ]
}

def lineScene = Scene {
    content: [ backgroundImageView, lineGroup ]
}

Stage {
    scene: lineScene;
    width: 200
    height: 232
    title: "Background Image"
}

// start the line animation.
timeline.play();

This code seems infinitely more obvious to me, but then again, I wrote it.

What do you think about the general hygiene of JavaFX script you’ve seen thus far? How should we better author our JavaFX script?