samedi 19 décembre 2009

Old School Scroll line

This is my first try to make an old school scroll line like on my old Atari ST.
Normally this is a bitmap effect, but in JavaFx all is nodes…
So I use Nodes :) and this image (from
http://forum.deltaforceteam.de/forum/viewtopic.php?f=3&t=127 )

The viewports variable is a sequence which contains for each character the portion of the image corresponding to it.
The line variable is a concatenation of the viewports of the letters to build the text shown in the screen.
And the timeline is used to scroll the text

I add a Reflection effect too :)


Main.fx
package scrolllinedemo;

/*
 * Main.fx
 *
 * Created on 12 déc. 2009, 00:08:52
 */
import javafx.stage.Stage;
import javafx.scene.Scene;

import javafx.geometry.Rectangle2D;

import javafx.scene.image.ImageView;

import javafx.scene.image.Image;

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.scene.effect.Reflection;

import javafx.scene.Group;
import javafx.scene.shape.Rectangle;
import javafx.scene.control.TextBox;
import javafx.scene.control.Button;

/**
 * @author Patrick
 */

def letterWidth = 32;
def letterHeight = 32;

var blankLine = [   0,   0,   0,   0,   0,   0,   0,  0,  0,  0 ];

var text = "Hello, this is my first old school scroll line demo. by Patrick ! ";

var textFont = StringConverter.stringToFont(text);
insert blankLine before textFont[0];
var textLength = sizeof(textFont)-1;
insert blankLine into textFont;

var textIncrement = 0;

var font = Image {
   url: "{__DIR__}img/font.png"
}

def viewports = for (row in [0..5]) {
   for (col in [0..9]) {
       Rectangle2D{
           minX: col * letterWidth, minY: row * letterHeight, height: letterWidth, width: letterHeight
       }
   }
}

var line = bind
   for (col in [0..10]) {   //10 chars (320 = 32*10)
      ImageView {
         x: bind (col * letterWidth) -scrollX , y:  0,
         viewport: bind viewports[textFont[col+textIncrement]]
         image: font
      }
   }

var scrollX =0;

var t = Timeline {
                repeatCount: Timeline.INDEFINITE
                keyFrames : [
                        KeyFrame {
                                time : 0s
                                canSkip : true
                        },
                        KeyFrame {

                                time : 8ms
                                canSkip : true
                                action: function(): Void{

                                  var tempX = scrollX+1;
                                  var tempTextIncrement = textIncrement;

                                   if (tempX>=32){
                                       tempX = 0;
                                     if (textIncrement<textLength)
                                        then tempTextIncrement++
                                        else tempTextIncrement = 0;

                                   }
                                   textIncrement = tempTextIncrement;
                                   scrollX = tempX;

                                }
                        }
                ]
        }

t.play();

var tb:TextBox;

Stage {
    title: "Application title"
    width: 335
    height: 150
    scene: Scene {
        content: [
                Group {
                    content:Group{
                        clip:Rectangle {
                                width:  320
                                height: 64
                            }
                        content: line
                    }
                    effect:Reflection {
                         fraction: 0.75
                         topOffset: 0.0
                         topOpacity: 0.5
                         bottomOpacity: 0.0
                    }
                  }
                
                tb = TextBox {
                        text: "try to type your text here... and press on GO!"
                        columns: 30
                        selectOnFocus: true
                        translateY:70
                }
                Button {
                        text: "GO!"
                        action: function() {
                            var textFontTemp = StringConverter.stringToFont(tb.text);
                            insert blankLine before textFontTemp[0];
                            textLength = sizeof(textFontTemp)-1;
                            insert blankLine into textFontTemp;

                            textFont = textFontTemp;
                            textIncrement =0;
                        }
                        translateY:70
                        translateX:275
                }
        ]
    }
}

download StringConverter.fx

dimanche 13 décembre 2009

Les jeux olympiques d’hiver de Vancouver choisissent JavaFX

J’entends dire ou des personnes me disent, ouais, JavaFx c’est bien mais il n’y a personne qui l’utilise, il n’y a pas de vraie référence, etc, etc …

C’est vrai que JavaFx est une technologie récente (la versions 1.0 n’est sortie que l’année dernière à cette même époque) et qu’il n’y a pas encore beaucoup de référence…
Mais cela commence à arriver, et cela commence avec les jeux olympiques d’hiver de Vancouver 2010 qui ont choisi JavaFX comme technologie de client riche !

On peut même déjà voir le résultat et essayer la première application disponible sur le site Vancouver2010 à l’adresse suivante : http://www.vancouver2010.com/olympic-medals/geo-view/

Cette application sert à montrer :

  • les médailles qui ont été gagné par les différents pays durant les différentes éditions des jeux olympiques d’hiver
  • quel athlète a gagné quelle médaille
  • Et plein d'autres choses...

C’est plutôt pas mal et ca marche super bien sur mon Mac (Snow Leopard), mon Linux (Ubuntu 9.10) et Windows Vista et Seven.

jeudi 10 décembre 2009

Glassfish V3, NetBeans 6.8, JavaFX Composer Preview

C’est noël avant l’heure ! :)

GlassFish V3 et NetBeans 6.8 sont sorties aujourd’hui …
Mais en plus, pour les personnes s’intéressant à JavaFX, la preview de JavaFX Composer (GUI Builder pour JavaFX) devrait être disponible le 15 décembre !!!

dimanche 29 novembre 2009

JavaFX Style Sheet with my custom component

Because my custom component doesn’t have a Mac look and feel (the rounded corners of the TextBox) and because I don’t want to change the code of my component, I decided to create a Mac Style Sheet.

Now, let us study the Mac style sheet and the code to use this style sheet

the Mac style sheet - mac.css

.searchTextBox {corner-radius: 25;padding-left: 10}

To have a rounded corners of the textbox, I use the CSS attribute corner-radius and to move the position of the input cursor (because of the rounded corners), I use the CSS attribute padding-left

To use this CSS just set the style sheets attribute of your Scene with the location of your CSS file.

    scene: Scene {
        stylesheets : ["{__DIR__}resources/mac.css"]

The code of the demo (the code of the custom component doesn’t change)

resources\mac.css
/* 
    Document   : style
    Created on : 3 oct. 2009, 00:34:05
    Author     : paddy
    Description:
        Purpose of the stylesheet follows.
*/

.searchTextBox {corner-radius: 25;padding-left: 10}
.searchTextBoxRed{corner-radius: 25;padding-left: 10;border-fill:#FF0000;}

Main.fx
/*
 * Main.fx
 *
 * Created on 3 oct. 2009, 00:44:05
 */

package customcomponentcssdemo;

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.text.Text;
import javafx.scene.text.Font;
import javafx.scene.control.Button;
import javafx.scene.control.RadioButton;

import javafx.scene.control.ToggleGroup;

/**
 * @author Patrick
 */

class ExtRadioButton extends RadioButton {
    public var action:function();

    override public var selected on replace {
        if (selected) {
            action();
        }
    }
}

def macStyleSheet="{__DIR__}resources/mac.css";
var stylesheets:String = macStyleSheet;

Stage {
    title: "Application title"
    width: 250
    height: 200

    var cssToggleGroup = ToggleGroup {};

    scene: Scene {
        //stylesheets : ["{__DIR__}resources/mac.css"]
        stylesheets : bind stylesheets
        content: [
            Text {
                font : Font {
                    size : 16
                }
                x: 10
                y: 30
                content: "Search "
            }
            SearchTextBox {
                    //styleClass : "searchTextBoxRed"
                    translateX :10
                    translateY :40
                    

                    onResetSearch:function(){
                        println("reset !");
                    }
                    onSearch:function(s: String){
                        println("Search of : {s}");
                    }
            }
            Button {
                    text: "just a button to change focus"
                    translateX :10
                    translateY :80
                    action: function() {
                        println("Hello !")
                   }
            }

            ExtRadioButton {
                            text: "do not use the CSS"
                            translateX :10
                            translateY :110
                            toggleGroup: cssToggleGroup
                           // selected: true

                            action: function() {
                                stylesheets = " ";
                            }
            }

            ExtRadioButton {
                            text: "use the CSS"
                            translateX :10
                            translateY :130
                            toggleGroup: cssToggleGroup
                            selected: true

                            action: function() {
                                stylesheets = macStyleSheet;
                            }
            }
        ]
    }
}

dimanche 15 novembre 2009

Les technologies RIA (Flex/Silvelight/JavaFX) hors et dans le navigateur

Toujours dans l'article sur les RIA du magazine Programmez n°124, il est dit que les 3 technologies RIA (Flex, Silverlight et JavaFX) peuvent fonctionner hors du navigateur et même que Silverlight hors du navigateur et équivalent à Adobe Air.
Que ces 3 technologies puissent fonctionner hors du navigateur est vrai, par contre dire que Silverlight hors du navigateur et équivalent à Adobe Air ce n'est pas vraiment exact.
Aussi, j'aimerais apporter quelques précisions (non exhaustives) sur ces technologies RIA hors du navigateur (et dans le navigateur aussi pour comparer ;) ).

Silverlight

Le mode hors du navigateur de Silverlight 3 est très limité, en effet une application Silverlight 3 s'exécutant hors du navigateur s'exécute aussi dans une sandbox, la même sandbox (et même runtime) que celle utilisé par le navigateur quand l'application s'exécute dans le navigateur.
L'application ne peut donc pas accéder aux ressources la machine ce qui est très limitant pour écrire une application desktop.
Pour écrire une vraie application desktop avec accès aux ressources de la machine, il faut utiliser WPF (uniquement Windows) qui diffère de Silverlight.
Microsoft propose d'ailleurs un très bon whitepaper comparant les WPF et Silverlight. http://wpfslguidance.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=28278

Flex AIR et Browser

Flex étant à la base une technologie pour écrire des applications s'exécutant dans le navigateur, Adobe a dû créer AIR pour pouvoir écrire des applications hors du navigateur.
Adobe AIR permet d'utiliser les ressources de la machine, contrairement à Silverlight, et d'écrire de vraies applications desktop en Flex.
Pourtant, il ne faut pas croire qu'une application écrite en Flex pour le navigateur va pourvoir fonctionner directement sur le desktop (hors du navigateur) car :
  • Le runtime Air est différents du player Flash pour le navigateur
  • Une application Flex/AIR est différente d'une application Flex pour le navigateur comme le montre ces 2 petits exemples de l'application Hello World pour Flex/AIR et Flex/navigateur.
    (Air utilise le tag WindowedApplication pour créer sa fenêtre alors qu'une application Flex utilise le tag Application)
Application Flex/AIR
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" title="Hello World">

<mx:Script>
<![CDATA[
         private function hello():void{
              myText.text = "Hello world !";
         }
      ]]>

</mx:Script>

<mx:Text id="myText" x="10" y="10" text="..." width="209"/>
<mx:Button x="154" y="36" label="Click me" enabled="true" click="hello()"/>

</mx:WindowedApplication>



Application Flex Brower
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">

<mx:Script>
<![CDATA[
         private function hello():void{
              myText.text = "Hello world !";
         }
      ]]>

</mx:Script>

<mx:Text id="myText" x="10" y="10" text="..." width="209"/>
<mx:Button x="154" y="36" label="Click me" enabled="true" click="hello()"/>

</mx:Application>

  • La compilation se fait avec la commande mxmlc pour une application Flex navigateur, alors qu'il faut utiliser amxmlc pour une application AIR.
    En fait amxml n'est qu'un batch qui exécute mxmlc avec l'option +configname=air pour compiler une application Flex/AIR avec ses spécificités.

JavaFX

JavaFX est la technologie la plus en avance sur l'exécution dans le navigateur et hors du navigateur. En effet, JavaFX :
  • utilise le même runtime (JRE) que se soit pour une application desktop ou pour une application dans le navigateur.
  • peut accéder, pour une application desktop, aux ressources de la machine comme une application java classique (lors son exécution dans le navigateur, il utilise une sandbox comme une applet java classique)
  • utilise le même SDK et les mêmes APIs que l'on écrive une application desktop ou une application pour le navigateur web. (le code JavaFx est identique dans les 2 cas)
  • utilise le même compilateur pour compiler une application JavaFX desktop ou navigateur (javafxc)
Récapitulatif
Runtimes navigateur et hors navigateur identiques Accès aux ressources de la machine en mode hors du navigateur Code identique pour écrire une application navigateur web ou desktop Compilateur/ SDK identiques
Silverlight oui non oui oui
Flex non oui non non
JavaFX oui oui oui oui

jeudi 5 novembre 2009

A propos de l'article sur les RIA du magazine Programmez n° 124

Je viens de lire l'article du magazine Programmez n°124 sur les RIA...
Et il y a plein de choses avec lesquelles je ne suis pas d'accord …
Mais le plus gros problème est l'exemple Hello World en JavaFX
Je ne sais pas ce qu'a voulu faire l'auteur, mais c'est clair qu'il ne connait pas JavaFX !

Pour pouvoir comparer un minimum les technos, j'ai donc réécrit cet exemple de deux manières différentes :

  • en utilisant le biding
  • en utilisant une méthode proche des exemples Flex et Silverlight

Et bien sûr, ces 2 exemples fonctionnent aussi bien dans le browser que en en dehors du browser

Méthode utilisant le binding

Main.fx
package helloworldprogrammez;

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.text.Text;
import javafx.scene.text.Font;
import javafx.scene.control.Button;

/**
 * @author patrick
 */
var text="...";

Stage {
    title: "Hello World"
    width: 400
    height: 200
    scene: Scene {
        content: [
            Text {
                font : Font {
                    size : 12
                }
                x: 10
                y: 10
                content: bind text
            }
            Button {
                text: "Click me"
                translateX: 154
                translateY: 36
                action: function() {
                    text="Hello world !"
                }
            }
        ]
    }
}

Méthode proche des exemples Flex et Silverlight

Main2.fx
package helloworldprogrammez;

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.text.Text;
import javafx.scene.text.Font;
import javafx.scene.control.Button;

/**
 * @author patrick
 */

Stage {
    var t:Text;

    title: "Hello World"
    width: 400
    height: 200
    scene: Scene {
        content: [
            t = Text {
                font : Font {
                    size : 12
                }
                x: 10
                y: 10
                content: "..."
            }
            Button {
                text: "Click me"
                translateX: 154
                translateY: 36
                action: function() {
                    t.content="Hello world !"
                }
            }
        ]
    }
}

mercredi 28 octobre 2009

Acquisition de Sun par Oracle – des news !

Oracle publie, sur son site, un nouveau document concernant l’acquisition de Sun.
Ce document parle entre autre :
• Du futur des produits (incluant NetBeans, GlassFish, OpenOffice, VirtualBox , MySQL, …)
• De l’offre Hardware et Software
• Et de l’open source.
Vous pouvez lire ce document ici
Tant que j’y suis, d’autres news de produits Sun (En Early Access ou Beta)
Java ME SDK 3.0 pour Mac OS est disponible ici . On peut enfin tester nos applications JavaFX mobile sur le mac ;)
Et NetBeans 6.8 Beta est disponible ici. Il supporte entre autre GlassFish V3 / Java EE 6 et JavaFX
Je vais enfin pouvoir faire des tests sur Java EE 6 (et GlassFish V3) sans avoir a bidouiller mon NetBeans 6.7.1 ;)

mercredi 14 octobre 2009

JavaFX Custom Component

This is just an example to show how you can create your own component from an existing component (here a TextBox).

For that, I wrote a new UI component which is a search text box like you can have it on Mac OS X. This search text box is just a TextBox enhanced with one method which is called each time you enter or delete a character and a small button to reset the criteria in the search text box.

The only problem is this line

override var style = "padding-right: 25 ";

which is not compatible with common profile, but I don’t know how to do that differently.

You can try this JavaFX custom component in the applet below

SearchTextBox.fx
/*
 * StatusField.fx
 *
 * Created on 3 oct. 2009, 00:44:26
 */

package customcomponent;

import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.control.TextBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;

import javafx.scene.shape.Rectangle;

import javafx.scene.input.MouseEvent;

import javafx.scene.input.KeyEvent;

/**
 * @author Patrick
 */

public class SearchTextBox extends TextBox {

    public var onResetSearch:function() : Void;
    public var onSearch:function(s: String) : Void;
    override var style = "padding-right: 25 ";

    override var styleClass = "searchTextBox";

    override var onKeyTyped = function(event:KeyEvent){
        if (this.rawText!=""){
            onSearch(this.rawText);
        }
    }
   
    public override function create():Node {
        var t = super.create();
        var resetButton:Group;

        var g = Group {
            content: [
                t,
                resetButton = Group {

                  var crossWidth = bind this.layoutBounds.height * 0.45;
                  var crossHeight = bind this.layoutBounds.height * 0.05;

                  visible : bind this.rawText.length() > 0
                  layoutX: bind this.layoutBounds.width - (this.layoutBounds.height / 2.0)
                  layoutY: bind this.layoutBounds.height / 2.0

                  content: 
                  [
                    Circle {
                        fill: Color.GRAY
                        radius:  bind this.layoutBounds.height * 0.325 // 65% of the texbox height and div 2 for the radius
                    }
                    Rectangle {
                        width: bind crossWidth
                        height:bind crossHeight

                        translateX: bind 0 - crossWidth / 2.0
                        translateY: bind 0 - crossHeight / 2.0

                        fill : Color.WHITE
                        rotate: 45
                    }
                    Rectangle {
                        width: bind crossWidth
                        height:bind crossHeight

                        translateX: bind 0 - crossWidth / 2.0
                        translateY: bind 0 - crossHeight / 2.0

                        fill : Color.WHITE
                        rotate: -45
                    }
                 ]

                 onMouseClicked: function (event:MouseEvent ){
                     commit();
                     this.text="";
                     onResetSearch();
                 }

             }
           ]
       }
       
       return g;
   }
}


Main.fx
/*
 * Main.fx
 *
 * Created on 3 oct. 2009, 00:44:05
 */

package customcomponent;

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.text.Text;
import javafx.scene.text.Font;
import javafx.scene.control.Button;

/**
 * @author Patrick
 */

Stage {
    title: "Application title"
    width: 250
    height: 200
    
    scene: Scene {
        content: [
            Text {
                font : Font {
                    size : 16
                }
                x: 10
                y: 30
                content: "Search "
            }
            SearchTextBox {
                    translateX :10
                    translateY :40
                    

                    onResetSearch:function(){
                        println("reset !");
                    }
                    onSearch:function(s: String){
                        println("Search of : {s}");
                    }
            }
            Button {
                    text: "just a button to change focus"
                    translateX :10
                    translateY :80
                    action: function() {
                        println("Hello !")
                   }
            }
        ]
    }
}

jeudi 24 septembre 2009

Duke Anim JavaFX


import java.util.Random;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
//import javafx.scene.effect.Reflection;
//import javafx.scene.effect.SepiaTone;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.Scene;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;

/**
 * @author paddy
 */

class DukeAnimModel {
  
    public var imageURL = "{__DIR__}images/duke.png";
  
    public var x = 0;
    public var y = 0;

    var xTarget = 0;
    var yTarget = 0;

    var xTemp = 0;
    var yTemp = 0;

    public var anim =
    Timeline {
        autoReverse: false
        keyFrames: [
            KeyFrame {
                time: 0s
                values: [x => xTemp, y => yTemp]

            },
            KeyFrame {
                time: 1s
                //values: [x => 300 tween Interpolator.LINEAR,y => yTarget tween Interpolator.LINEAR]
                values: [x => xTarget tween
                    Interpolator.SPLINE(0,.5,.5,1),y => yTarget tween
                    Interpolator.SPLINE(0,.5,.5,1)]

                action: function(): Void{
                    println("at 1 second ...");

                    var generator = new Random();
                
                    //xTarget = (generator.nextFloat() * 400 + 1) as Integer;
                    xTarget = (
                    generator.nextFloat() * 240 + 1) as Integer;
                    println("xNext {xTarget} ");
                    //yTarget = (generator.nextFloat() * 300 + 1) as Integer;
                    yTarget = (
                    generator.nextFloat() * 320 + 1) as Integer;
                    println("yNext {yTarget} ");
                    xTemp=x;
                    yTemp=y;

                }
            },
        ]
        repeatCount: Timeline.INDEFINITE
    };
}

var dukeAnimModel = DukeAnimModel {}

dukeAnimModel.anim.play();


Stage {
    title: "Application title"
    width: 240  //400
    height: 320 //300
    scene: Scene {
        content: ImageView {
            transforms: Translate {
                x: bind dukeAnimModel.x
                y: bind dukeAnimModel.y
            }
            image: Image {
                url: dukeAnimModel.imageURL

            }
          /*  effect:Reflection{
                input: SepiaTone{
                }
            }
           */

        }
    }
    onClose: function(){
        println("exit");
    }
}