I- SwingComponent et CustomNode : la base de travail

JavaFX inclut une classe, SwingComponent, permettant d'établir un lien entre le monde Swing et le nouveau monde JavaFX. Cette classe, dont la documentation est disponible à cette URL http://java.sun.com/javafx/1/docs/api/javafx.ext.swing/javafx.ext.swing.SwingComponent.html, propose parmi ses méthodes, la méthode wrap() admettant en unique paramètre un objet de type javax.swing.JComponent. Comme son nom l'indique, cette méthode va encapsuler le composant Swing et retourner un SwingComponent, lui même héritant de javafx.scene.Node. Ce composant pourra ensuite être intégré à une composition graphique classique de JavaFX. Le principe est donc très simple, la mise en oeuvre aussi.

Attardons nous un peu sur CustomNode. Cette classe est dédiée à être étendue pour servir de base à des composants qui seront constitués d'autres composants. A ce titre, parce que SwingComponent va retourner un composant de type Node, le CustomNode sera un parfait wrapper pour accueillir celui-ci et y ajouter des fonctionnalités.

La base de départ sera donc ainsi constituée :

Une classe étendant CustomNode surchargeant sa méthode create(), laquelle retournera un objet héritant de Node, en l'occurence le composant issu de Swing wrappé par SwingComponent.

Voici un exemple de code mettant en oeuvre cette approche :

[...]

public class ChartParameters {
    public var properties : Boolean = true;
    public var save : Boolean = true;
    public var print : Boolean = true;
    public var zoom : Boolean = true;
    public var tooltips : Boolean = true;
    public var legend : Boolean = true;
    public var urls : Boolean = true;
}

[...]

public class MyFirstSoGreatChartFX extends CustomNode {

  protected var swingComponent : SwingComponent;
  protected var dataset : PieDataset;

  public var properties : Boolean = true;
  public var save : Boolean = true;
  public var print : Boolean = true;
  public var zoom : Boolean = true;
  public var tooltips : Boolean = true;
  public var legend : Boolean = true;
  public var urls : Boolean = true;
  public var dimension : Dimension;

[...]

  protected override function getDataset() {
      if (dataset == null) {
          dataset = new DefaultPieDataset();
      }
      return dataset;
  }

  public override function create() : Node {
        var chart = ChartFactory.createPieChart3D(
            "title",
            getDataset() as PieDataset,
            "legend",
            "tooltips",
            "urls"
        ) as Chart;

        var chartPanel = new ChartPanel(chart,
                  this.dimension.width,
                  this.dimension.height,
                  this.dimension.width,
                  this.dimension.height,
                  this.dimension.width,
                  this.dimension.height,
            true, //boolean useBuffer
            properties,
            save,
            print,
            zoom,
            tooltips,
        ) as ChartPanel;
        swingComponent = SwingComponent.wrap(chartPanel);
        return swingComponent;
  }

}

Ce composant peut ensuite être manipulé comme tout autre dans JavaFX.

Exemple :

[...]
var pieDataSetTest : DefaultPieDataset = new DefaultPieDataset();
pieDataSetTest.setValue("val1", new Double(10));
pieDataSetTest.setValue("val2", new Double(10));
pieDataSetTest.setValue("val3", new Double(5));
pieDataSetTest.setValue("val4", new Double(30));
pieDataSetTest.setValue("val5", new Double(20));

Stage {
    title: "When charts come to JavaFX"
    width: 800
    height: 800
    scene: Scene {
        content: Group {
            content: [
                Text {
                    content: "When charts come to JavaFX"
                    font: Font {
                        size: 24
                    }
                },
                MyFirstSoGreatChartFX {
                    translateY: 20
                    dataset: bind pieDataSetTest;
                    title: "Super Pie Chart"
                    dimension: bind new Dimension(400, 300)
                    backgroundPaint: Color.WHITE
                }
            ]
        }
    }
}

Nous obtenons avec cet exemple une mise en oeuvre très simple d'un PieChart. Il ne reste ensuite qu'à étendre la librairie avec les autres types de charts.


II- Le binding : pratique et efficace

Parmi les caractéristiques fortes de JavaFX figure celle du binding, permettant ce qui a fait défaut à Swing et que d'autres frameworks de présentation proposent, cad établir un lien automatique entre un objet représentant le modèle et l'objet graphique, représentant la vue. Ainsi la synchronisation des données entre les deux couches se fait de façon transparente, en mode unidirectionnel ou bidirectionnel selon la configuration.

L'exemple du point précédent montre la mise enoeuvre du binding entre le composant graphique MyFirstSoGreatChartFX et le Dataset (ainsi que la Dimension). Ce type de binding va permettre de modifier les données du dataset et d'obtenir une mise à jour du PieChart synchronisée sans rien avoir à ajouter d'autre que la déclaration "bind". Simple et efficace, donc élégant :-)


III- L'approche native

Une approche plus pérenne serait de ne pas avoir à wrapper une librairie existante mais plutôt de disposer de composants natifs riches, pouvant être étendus par la suite pour coller à des besoins plus fins. Bien sûr JavaFX propose des composants pouvant être étendus mais ils sont d'assez bas niveau fonctionnel et demandent un effort assez conséquent pour obtenir des composants de haut niveau. On peut espérer que la librairie par défaut s'étoffe en ce sens et permette pourquoi pas, aux équipes développant les libraries actuelles de les porter en composants natifs pour JavaFX.


IV- Conclusion

JavaFX offre les facilités pour construire une librairie de composant avancés, ces composants qu'on aurait voulu voir livrés de série. L'effort pour réaliser une librairie est bien sûr non neutre mais pas insurmontable non plus. Tout dépend de l'intérêt que la communauté portera à ce petit dernier des frameworks pour RIA. Si cela accroche, il est fort probable que le panel des composants connaisse un essort rapide. Il est aussi à noter que le wrapping n'est utile que pour les applications JavaFX pour desktop. Le profil mobile ne supportant pas le wrapping de composant Swing. Créateurs d'applis mobiles, passez votre chemin pour le moment :-(

En attendant, vous pouvez retrouver quelques composants que j'ai commencé à réaliser ici :


http://code.google.com/p/javafxwidgetlib/

ainsi qu'une démo simpliste :


http://javafxwidgetlib.googlecode.com/svn/trunk/JavaFXWidgetLib-demo/JavaFX-Widgets-Library.html

Toute participation est la bienvenue :-)