janvier
2012
Après une dizaine de jours de C# et de taches administratives, un bref retour sur du FX. Oracle a un peu laisser tomber ses plans initiaux de fournir des convertisseurs de code et de projet permettant une migration de FX 1.x vers FX 2.x. C’est d’autant plus ennuyeux que tant la Production Suite, avec ses convertisseurs pour SVG, Illustrator et Photoshop, que le JavaFXComposer permettaient de faire des trucs sympas avec le format FXD/FXZ.
S’il a été assez rapide de recoder à la main certaines UI en 2.0 (pas la peine de se forcer à faire du FXML pour ça tant que l’éditeur visuel n’est pas disponible), il n’en est pas de même pour les SVG.
Une idée serait de partir du SVG originel et de le transformer directement en FXML. Déjà, on se souvient que le convertisseur SVG -> FXD de la ProductionSuite n’était pas parfait : le boulot était tout à fait honorable mais certains choses comme les gradients circulaires transformés n’étaient pas vraiment bien supportés (voir pas supportés du tout).
En plus, il se trouve que les SVG produits par Inkscape ne sont pas totalement propres, par exemple certaines formes ou groupes contiennent des matrices de transformations au lieu de leur positionnement absolu dans le design et donc il leur faut appliquer ces transformations pour avoir leurs vraies coordonnées. Et je n’ai pas vraiment envie de ressortir les bouquins de math de terminale ou de DEUG sur les calculs matriciels, rien que d’évoquer ces souvenirs est suffisamment pénible en soit. En plus pour peu que l’on ait fait un peu joujou avec Photoshop, on aura pas forcement de SVG sous la main (et tout le monde n’a pas forcement envie de savoir comment un fichier PSD c’est fait). Et puis bon… avoir le support du SVG en Java pur, ça veut dire Batik et tout un tas de libs plus ou moins lourdes à télécharger ou à utiliser. Donc pour porter certaines parties de nos projets vers FX 2.0, il va falloir transformer tout cela en FXML, mais sans pour autant devoir code un monstre au niveau du convertisseur (si Oracle n’y arrive pas, moi j’ai peu de chances…)
Donc, autant s’attacher a faire un convertisseur un peu plus « universel », mais aussi plus léger.
Apparemment, je ne suis pas le seul a avoir ce genre de problèmes lors du portage d’anciens projets, et il y a plusieurs topics dans les forums d’Oracle sur ce genre de conversion. L’astuce suggérée est assez simple à mettre en place : il faut tout simplement charger le fichier FXD/FXZ dans un projet écrit en FX 1.x, récupérer le nœud racine, parcourir arborescence de son contenu et générer le code XML approprié pour chaque nœud rencontré, ainsi que ses attributs (pour ceux qui ont des équivalents en 2.0).
Ça donne un truc comme ça qui peut servir de base de départ pour faire un convertisseur supportant plus de type de nœuds (ici seuls Group et SVGPath sont convertis) :
def path: String = file.toURI().toString();
def fxdNode: com.sun.javafx.tools.fxd.FXD = FXDLoader.load(path) as com.sun.javafx.tools.fxd.FXD;
def charset: String = "UTF-8";
def output: PrintWriter = new PrintWriter("{file.getName().substring(0, file.getName().lastIndexOf('.'))}.fxml", charset);
try {
output.println("");
output.println();
output.println("");
output.println("");
output.println("");
output.println("");
output.println("");
output.println("");
convert(fxdNode, output, "");
} finally {
output.close();
}
function convert(node: Node, output: PrintWriter, suffix: String): Void {
println("{node.id}\t{node.getClass()}");
if (node instanceof com.sun.javafx.tools.fxd.FXD) {
convertFXD(node as com.sun.javafx.tools.fxd.FXD, output, suffix);
} else if (node instanceof Group) {
convertGroup(node as Group, output, suffix);
} else if (node instanceof SVGPath) {
convertSVGPath(node as SVGPath, output, suffix);
}
}
function convertFXD(fxd: com.sun.javafx.tools.fxd.FXD, output: PrintWriter, suffix: String): Void {
output.print("{suffix}");
output.println("{suffix} ");
for (child in fxd.content) {
convert(child, output, "{suffix} ");
}
output.println("{suffix} ");
output.println("{suffix}");
}
function convertGroup(group: Group, output: PrintWriter, suffix: String): Void {
output.print("{suffix}");
output.println("{suffix} ");
for (child in group.content) {
convert(child, output, "{suffix} ");
}
output.println("{suffix} ");
output.println("{suffix}");
}
function convertSVGPath(svgPath: SVGPath, output: PrintWriter, suffix: String): Void {
output.print("{suffix}");
}
function convertNodeAttributes(node: Node, output: PrintWriter): Void {
if (node.id != null) {
output.print(" id=\"{node.id}\"");
}
if (node.styleClass != null) {
output.print(" styleClass=\"{node.styleClass}\"");
}
if (node.style != null) {
output.print(" style=\"{node.style}\"");
}
if (not node.blocksMouse) {
output.print(" mouseTransparent=\"true\"");
}
if (node.opacity != 1) {
output.print(" opacity=\"{node.opacity}\"");
}
if (not node.visible) {
output.print(" visible=\"false\"");
}
if (node.disable) {
output.print(" disable=\"true\"");
}
if (node.translateX != 0) {
output.print(" translateX=\"{node.translateX}\"");
}
if (node.translateY != 0) {
output.print(" translateY=\"{node.translateY}\"");
}
if (node.translateZ != 0) {
output.print(" translateZ=\"{node.translateZ}\"");
}
if (node.rotate != 0) {
output.print(" rotate=\"{node.rotate}\"");
}
if (node.scaleX != 1) {
output.print(" scaleX=\"{node.scaleX}\"");
}
if (node.scaleY != 1) {
output.print(" scaleY=\"{node.scaleY}\"");
}
if (node.scaleZ != 1) {
output.print(" scaleZ=\"{node.scaleZ}\"");
}
}
function convertShapeAttributes(shape: Shape, output: PrintWriter): Void {
if (shape.fill != null) {
output.print(" fill=\"{paint2FXML(shape.fill)}\"");
}
if (shape.stroke != null) {
output.print(" stroke=\"{paint2FXML(shape.stroke)}\"");
}
if (shape.strokeWidth != 0) {
output.print(" strokeWidth=\"{shape.strokeWidth}\"");
}
}
function paint2FXML(paint: Paint): String {
return "black";
}
Je n’a pas encore lu comment sont déclarées les Paint en FXML donc pour le moment ça sort tout en noir ; un de ces quatre il me faudra aller lire les specs….
Il faudra faire un peu plus de boulot pour adapter des concepts plus complexes du FXZ comme le fait qu’il peut embarquer des fichiers images ou des media et extraire ces donnés dans des fichiers accessibles par le FXML via le CLASSPATH.
Par chance, ce n’est le cas d’aucun des fichiers qu’il me faut convertir. En tout cas, pour ce simples SVGPath, le code rapide à écrire et la conversion est efficace.
Évidement dans le cas de contrôles, tout n’est pas convertible à 100%, mais le résultat fourni est une bonne base de travail (et puis comme indiqué plus haut, on pourra corriger cela via l’éditeur FXML quand il sera disponible).
Il reste à espérer qu’un jour Oracle fournisse ENFIN un convertisseur de SVG vers FXML (pour le moment pas d’équivalent à la Production Suite à l’horizon) et surtout rajoute directement le support du SVG dans JavaFX 2.0.
Apparemment on peut afficher du SVG via WebView (pas vraiment pu tester pour le moment) mais même si cela marche, c’est loin d’offrir le même confort d’usage que d’avoir un accès direct aux divers nœud graphiques du design.
Même si je fais encore faire de la maintenance de code FX 1.x pendant quelques temps, c’est probablement l’un des derniers projets que j’ai créé pour ce framework et ce langage. Ça fait bizarre…
1 Commentaire + Ajouter un commentaire
Commentaires récents
- Back from the future… dans
- Back from the future… dans
- Static linking = does not Compute dans
- Paquetage x 2 dans
- Why you little… dans
[…] FXD -> FXML par bouye (13/01/2012 03:59) Après une dizaine de jours de C# et de taches administratives, un bref retour sur du FX. Oracle a un peu laisser tomber ses plans initiaux de fournir des convertisseurs de code et de projet permettant une migration de FX 1.x vers FX 2.x. C’est d’autant plus ennuyeux que tant la Production Suite, avec ses convertisseurs pour SVG, Illustrator et Photoshop, que le JavaFXComposer permettaient de faire des trucs sympas avec le format FXD/FXZ. S’il […] […]