Une question dans les forums Sun (Color chart reference - see this thread if you don't read French!) m'a motivé pour écrire un programme non trivial en JavaFX, affichant les couleurs pré-définies (statiques) de JavaFX avec leur noms. Rien de très gros, mais c'est ma première utilisation de la réflexion dans ce langage, et un rare (à ce jour) exemple d'utilisation de cette réflexion (en dehors de l'intégration à Java).
En voyant les programmes postés, j'ai été ennuyé de voir la liste codée en dur : cela a dû être long à taper (où à générer à partir de la doc, peut-être), ça grossit pas mal le programme, et surtout c'est susceptible de ne pas être à jour dans une version ultérieure.
J'ai trouvé dommage que Sun ait créé ces couleurs à l'ancienne (pré-Java 1.5), sans utiliser d'enum itérable. J'ai donc pensé que je pourrais utiliser la réflexion pour avoir automatiquement cette liste de couleurs.
J'ai un peu sué, il y a peu d'exemples d'utilisation de cette API sur le Net. Mais j'ai fini par m'en sortir, j'espère que mes expériences seront utiles à quelqu'un.
J'ai aussi joué avec les custom nodes et j'ai transposé quelques fonctions que j'avais fait en Java pour vérifier que le contraste de couleurs (texte sur fond) est suffisant.
Ce qui fait que le code reste assez gros !
Je le donne ci-dessous pour faciliter la consultation, il est aussi disponible sur mon dépôt Launchpad de scripts JavaFX.
/*
* Display the named colors of JavaFX
*
* Author: Philippe Lhoste <PhiLho(a)GMX.net> http://Phi.Lho.free.fr
*
* Copyright notice: See the PhiLhoSoftLicence.txt file for details.
* This file is distributed under the zlib/libpng license.
* Copyright (c) 2009 Philippe Lhoste / PhiLhoSoft
*/
/* File history:
* 1.00.000 -- 2009/03/19 (PL) -- Creation
*/
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.Node;
import javafx.scene.CustomNode;
import javafx.scene.Group;
import javafx.scene.text.*;
import javafx.scene.shape.*;
import javafx.scene.paint.Color;
import javafx.reflect.*;
import java.lang.Math;
var content: Node[];
def RADIUS = 70;
def COL_NB = 8;
def MARGIN = 10;
class ColorSample extends CustomNode
{
public var index: Integer;
public var name: String;
public var color: Color;
public-read var x: Integer;
public-read var y: Integer;
override public function create(): Node
{
var w = 2 * RADIUS;
var h = RADIUS / 2;
x = MARGIN + w/2 + w * (index mod COL_NB);
y = MARGIN + h/2 + h * (index / COL_NB);
var text = Text
{
x: x
y: y + 4
fill: Color.BLACK
content: name
font: Font { size: 10 }
}
// Workaround to center in ellipse
var tw = text.boundsInLocal.width;
text.translateX = -tw / 2;
// If dark color, use white text
if (not IsColorConstrastOK(color, Color.BLACK) and color.opacity != 0.0)
{
text.fill = Color.WHITE;
}
Group
{
content:
[
Ellipse
{
centerX: x
centerY: y
radiusX: w / 2
radiusY: h / 2
fill: color
stroke: null
}
text
]
}
}
}
Stage
{
title: "Show Named Colors"
visible: true
scene: Scene
{
width: 2 * MARGIN + COL_NB * 2 * RADIUS
height: 800
fill: Color.WHITE
content: bind content
}
}
def localContext: FXLocal.Context = FXLocal.getContext();
def type: FXClassType = localContext.findClass("javafx.scene.paint.Color");
var colorInstance = type.allocate();
def variables = type.getVariables(true);
var index = 0;
for (i in [ 0 ..< variables.size() ])
{
def vm: FXVarMember = variables.get(i);
def bStatic = vm.isStatic();
def name = vm.getName();
def fullName = vm.toString();
if (bStatic)
{
def value: FXValue = vm.getValue(colorInstance);
var localValue = value as FXLocal.ObjectValue;
var color = localValue.asObject();
if (color != null)
{
var colorSample = ColorSample
{
index: index++
color: color as Color
name: name
};
insert colorSample into content;
//~ println("Static: {name} is {fullName} Value: {color}");
}
else
{
println("Static: {name} is {fullName}");
}
}
else
{
println("{name} is {fullName}");
}
}
// Adaptation of Java code I wrote, based on http://www.wat-c.org/tools/CCA/1.1/ &
// http://www.paciellogroup.com/resources/contrast-analyser.html
function IsColorConstrastOK(c1: Color, c2: Color): Boolean
{
return GetLuminanceContrastRatio(c1, c2) > 4.5;
}
function GetLuminanceContrastRatio(c1: Color, c2: Color): Number
{
var l1 = GetColorLuminance(c1);
var l2 = GetColorLuminance(c2);
var ll = Math.min(l1, l2);
var hl = Math.max(l1, l2);
return (hl + 0.05) / (ll + 0.05);
}
function GetColorLuminance(c: Color): Number
{
return
0.2126 * GetLinearisedColorComponent(c.red) +
0.7152 * GetLinearisedColorComponent(c.green) +
0.0722 * GetLinearisedColorComponent(c.blue);
}
function GetLinearisedColorComponent(fcc): Number
{
if (fcc <= 0.03928)
return fcc / 12.92;
return Math.pow((fcc + 0.055) / 1.055, 2.4);
}
J'ajoute une copie d'écran pour avoir une idée du résultat final.