Tuning In With MyTVSchedule

By Kuldip Pabla, March 22, 2010

MyTVSchedule uses the JavaFX common profile to display a TV guide over 24 hours. The sample demonstrates the capabilities of JavaFX by building a scheduler that uses graphics, animation and control. The user can mouse over on the horizontal or vertical axis to view a detail schedule for selected channels over a period of 24 hours. The view is enhanced by using pop-up images with a multiline text display to display context-sensitive infromation in detail.

Understanding the Code

MyTVSchedule uses the ToolBar container to add control buttons that implement a control panel. ToolBar is one of the JavaFX SDK 1.3 preview UI controls in com.sun.javafx.scene.control.ToolBar. Figure 1 displays the code fragment of the Panner class that implements the control panel.

Source Code
public class Panner extends CustomNode{

    ....

    override public function create():Node {
        ToolBar {
            styleClass:"toolbar"
            items: [
                imageButton("upArrow", "upArrow"),
                imageButton("downArrow", "downArrow"),
                imageButton("leftArrow", "leftArrow"),
                imageButton("rightArrow", "rightArrow"),
            ]
        }
    }
}

Figure 1: Panner Class

The GuideNode class implements the main functions for this sample. The data is set up on a grid formed by rows and columns. Vertical and horizontal scrolling are implemented by using the scrollview function. javafx.scene.control.ScrollBarPolicy and javafx.scene.control.ScrollView help you set up and control scrolling. On mouse events, the panMouseHandler function implements horizontal scrolling. When the mouse curso hovers over a schedule, an image pops up with multiline text display. These capabilities are implemented by using the public classes- javafx.scene.text.Text .

Source Code
 
public class GuideNode extends CustomNode {

    ....
    
    
    function panMouseHandler (e:MouseEvent) {
        println ("e {e.node.id}");
        if (e.node.id == "rightArrow" ){
            println ("{horiScroll.hvalue}: {horiScroll.hmin} : {horiScroll.hmax}");
            if (horiScroll.hvalue == horiScroll.hmax){
                horiScroll.hvalue += .10;
                dataScroll.hvalue += .10;
            }
        } else if (e.node.id == "leftArrow") {
            if (horiScroll.hvalue = horiScroll.hmin){
                horiScroll.hvalue -= .10;
                dataScroll.hvalue -= .10;
            }
        }
    }

    var vertScroll = ScrollView {
        styleClass: "scroller"
        translateY:26
        node : rowButtons
        vbarPolicy: ScrollBarPolicy.NEVER;
        hbarPolicy: ScrollBarPolicy.NEVER;
        layoutInfo: LayoutInfo { width: 115 height: 280 }
        
    };
    var horiScroll = ScrollView {
        styleClass: "scroller"
        translateX:115
        node: colButtons;
        vbarPolicy: ScrollBarPolicy.NEVER;
        hbarPolicy: ScrollBarPolicy.NEVER;
        layoutInfo: LayoutInfo { width: 680 height: 26 }
        
    }
    var dataScroll = ScrollView {
        styleClass: "scroller"
        translateX: 115
        translateY:26
        node: dataGrid
        vbarPolicy: ScrollBarPolicy.NEVER;
        hbarPolicy: ScrollBarPolicy.NEVER;
        layoutInfo: LayoutInfo { width: 680 height: 280 }
        
    }


    override function create() : Node {
        generateTimeTable ();
        blocksMouse = true;
        var corner = Rectangle {
            translateY: 0
            visible:true
            width:115
            height: 26
            
            fill: Color.web ("0x415170", 0.0);
            stroke: Color.web ("0x415170");
         }
        var text: Text = Text {
            content: "CHANNEL"
            translateX: bind (corner.width - text.boundsInLocal.width) / 2.0
            translateY: bind (corner.height - text.boundsInLocal.height) / 2.0 + 3
            textOrigin: TextOrigin.TOP
            textAlignment: TextAlignment.CENTER
            styleClass: "channelText"
        }
 

        ...

    }
}

Figure 2: GuideNode Class

The ImageThumbnail class implements the pop-up node for a detail channel infomration. The pop-up appears on entering a cell with a javafx.animation.transition.ScaleTransition animation. The pop-up hides with a the reverse ScaleTranstion animation.

Source Code
 
public class ImageThumbnail extends CustomNode {
    
	....

    public var imageView = ImageView {
        layoutX: 7
        layoutY: 7
        fitWidth: 231;
        fitHeight: 130
        preserveRatio: true
        styleClass: "thumbnail";
    };

    // used for popups
    var scaleTransition = ScaleTransition {
        duration: .5s
        node: bind this;
        fromX: 0.0 toX: 1.0
        fromY: 0.0 toY: 1.0
        autoReverse: false
    }

    // used for hiding popups
    var reverseTransition = ScaleTransition {
        duration: .5s
        node: bind this;
        fromX: 1.0 toX: 0
        fromY: 1.0 toY: 0
        autoReverse: false
    }

    public function imageMouseOver(mouseX: Number, mouseY: Number) {
        var mY = mouseY;
        var mX = mouseX;
        if (mouseY + height > Main.cStageHeight) {
            mY = mouseY - height;
        }
        if (mouseX + width > Main.cStageWidth){
            mX = mouseX - width;
        }

        this.layoutX = mX;
        this.layoutY = mY;
        group.visible = true;
        scaleTransition.playFromStart();
    }

    public function imageMouseExit () {
        scaleTransition.rate = -1;
        reverseTransition.playFromStart();
    }

    ....
	
}

Figure 3: Pop-up with ScaleTransition