CSSEngine API

Toutes les implémentations des moteurs CSS SWT , moteurs CSS Swing (ou autres) implémente l'interface org.akrogen.tkui.css.core.engine.CSSEngine qui fournit des méthodes qui permettent entre autres de :

  • charger des feuilles de styles CSS à l'aide de la méthode parseStyleSheet .
  • d'appliquer des styles a des widgets à l'aide de la méthode applyStyles .

Les moteurs CSS s'appuient sur différents concepts qui rendent les moteurs CSS configurables comme :

  • choix du parser SAC à utiliser
  • gestion des Selectors (SWT/Swing ou HTML...)
  • ajout de ses propres ICSSPropertyHandler pour appliquer une propriété CSS personnalisée.

CSSEngine Methods

TK-UI implémente l'interface générique de moteur CSS org.akrogen.tkui.css.core.engine.CSSEngine qui propose plusieurs méthodes :

  • parseStyleSheet (*) : cette méthode permet de charger des feuilles de styles CSS en interne.
  • applyStyles (*) : cette méthode permet d'appliquer des styles CSS (toutes les propriétés CSS) provenant des feuilles de styles CSS chargées, sur des widgets (Swing, SWT...).
  • parseStyleDeclaration (*) : cette méthode permet de charger une déclaration d'un style CSSStyleDeclaration .
  • applyStyleDeclaration (*) : cette méthode permet d'appliquer une déclaration d'un style sur des widgets (Swing, SWT...).
  • parsePropertyValue (*) : cette méthode permet de parser une valeur String en instance CSSValue .
  • applyCSSProperty (*) : cette méthode permet d'appliquer la valeur d'une propriétés CSS à une widget.
  • retrieveCSSProperty (*) : cette méthode permet de retrouver la valeur d'une propriété CSS d'une widget.
  • reset : cette méthode permet de réinitiaiser les feuilles de styles CSS parsées.
  • dispose : cette méthode permet de libérer les ressources (Color, Font...) créé par le moteur CSS et appelle la méthode reset.

Les examples suivants s'appuient sur une instance CSSEngine engine . Cette instance peut etre de type Swing :

CSSEngine engine = new CSSSwingEngineImpl();
ou SWT :
Display display = new Display();
CSSEngine engine = new CSSSWTEngineImpl(display);

parseStyleSheet

Cette méthode permet de charger une feuille de style CSS. Voici un exemple de code d'utilisation de cette méthode :

CSSEngine engine = ....
engine.parseStyleSheet(new StringReader("input{color:red;} textarea{color:green}"));

parseStyleSheet retourne une instance w3c StyleSheet qui est stockée en interne dans une instance w3c DocumentCSS de engine. DocumentCSS peut être accéssible via la méthode getDocumentCSS :

CSSEngine engine = ...;
DocumentCSS documentCSS = engine.getDocumentCSS();

applyStyles

Cette méthode permet d'appliquer les styles CSS (chargés préalablement à l'aide de la méthode parseStyleSheet ) à une widget (et ses widgets enfants). Voici un exemple de code qui applique les styles CSS chargées à une JFrame Swing :

CSSEngine engine = ....
// Apply style declaration to the JFrame and the components children
engine.applyStyles(frame, true);
Le premier paramètre de la méthode indique que le moteur CSS doit appliquer les styles CSS à la JFrame. Le deuxième paramètre vaut true et indique que que les widgets enfants à la JFrame doivent être itérés et pour chacune des widgets enfants, les styles CSS doivent être appliqués.

parseStyleDeclaration

Cette méthode permet de parser un style inline et de retourner une instance CSSStyleDeclaration . Voici un exemple de code d'utilisation de cette méthode :

CSSEngine engine = ....
// Create CSSStyleDeclaration
CSSStyleDeclaration styleDeclaration = engine.parseStyleDeclaration("color:red;background-color:green");

applyStyleDeclaration

Cette méthode permet d'appliquer une déclaration d'un style CSSStyleDeclaration à une widget. Voici un exemple de code d'utilisation de cette méthode :

Text text = ...;
CSSEngine engine = ....
// Create CSSStyleDeclaration
CSSStyleDeclaration styleDeclaration = engine.parseStyleDeclaration("color:red;background-color:green");
// Apply style declaration to the Text widget
engine.applyStyleDeclaration(text, styleDeclaration);

L'application d'une declaration de style peut s'effectuer de la même manière à l'aide de la méthode parseAndApplyStyleDeclaration :

Text text = ...;
CSSEngine engine = ....
// Create CSSStyleDeclaration and apply style declaration to the Text widget
engine.parseAndApplyStyleDeclaration(text, "color:red;background-color:green");

parsePropertyValue

Cette méthode permet de parser une valeur String en instance CSSValue . Voici un exemple de code qui permet de récupérer une instance CSSValue :

CSSEngine engine = ...;
CSSValue value = engine.parsePropertyValue("red");

applyCSSProperty

Cette méthode permet d'appliquer la valeur CSS d'une propriété CSS à une widget. Voici un exemple de code d'utilisation de cette méthode :

Text text = ...;
CSSEngine engine = ...;
CSSValue value = engine.parsePropertyValue("red");
engine.applyCSSProperty(text, "color", value, null);

retrieveCSSProperty

Cette méthode permet de retrouver la valeur CSS (String) d'une propriété CSS d'une widget. Voici un exemple de code d'utilisation de cette méthode :

Text text = ...;
CSSEngine engine = ...;
String value = engine.retrieveCSSProperty(text, "color");

reset

Cette méthode permet de reinitialiser les feuilles de styles CSS et de supprimer les listeners ajoutés aux widgets lorsque gérer les pseudo classes dynamiques (:focus, :hover...).

dispose

Cette méthode appelle la méthod reset et appelle la méthode dispose du IResourcesRegistry enregistré dans l'instance CSSEngine, ce qui permet de libérer les ressources créées et mises en cache par le moteur CSS.

CSSEngine Concepts

Les moteurs CSS sont configurables et sont basés sur plusieurs concepts :
  • Gestion des chemins des ressources comme les URL des images à l'aide de IResourcesLocatorManager .
  • Gestion des caches, libération des ressources (Color, Font...) à l'aide de IResourcesRegistry
  • Configuration des noms des Selectors (HTML, Swing, SWT), des informations id, style, class à l'aide de IElementProvider et CSSStylableElement .
  • L'application d'une valeur d'une propriété CSS sur une widget s'effectue à l'aide des ICSSPropertyHandler .
  • La conversion d'une instance CSSValue en object Color, Font... s'effectue à l'aide des converters ICSSValueConverter .

IResourcesLocatorManager

Le moteur CSS est capable de gérer des url sur des ressources. Voici un exemple SWT de style que l'on peut ecrire pour ajouter une image dans un bouton.

Button {
    background: url(./images/icons/type/class.gif);
}

Une image doit donc être chargée à partir de l'url défini. Pour résoudre les url et charger les resources, le moteur CSS utilise une implémentation de org.akrogen.core.resources.IResourcesLocatorManager qui permet de résoudre l'url et retourner un java.io.Reader ou java.io.InputStream .

Une instance IResourcesLocatorManager est constituée d'une liste de org.akrogen.core.resources.IResourceLocator qui permet de retourner un java.io.Reader ou java.io.InputStream à partir d'un uri.

Par défaut l'instance IResourcesLocatorManager utilisé dans TK-UI est org.akrogen.core.impl.resources.ResourcesLocatorManager qui contient une instance org.akrogen.core.impl.resources.FileResourcesLocatorImpl qui permet de resoudre les url de type file.

Pour enregistrer l'implémentation de IResourcesLocatorManager a utiliser par le moteur CSS, la méthode setResourcesLocatorManager doit être appelée.

public void setResourcesLocatorManager(IResourcesLocatorManager resourcesLocatorManager);

IResourcesRegistry

Lorsque le moteur CSS doit appliquer le style :

Label {
    color:red;
}
Button {
    background-color: red;
}

Le moteur CSS utilise un parseur w3c SAC pour parser les styles CSS. Il transforme la string red en instance w3c CSSValue . La couleur red doit etre ensuite appliquée aux widgets Label et Button, autrement dit le CSSValue red doit etre transformé en instance Color de l'UI :

Il existe deux problèmes lorsque l'on manipule des ressources :

  • cache des ressources : dans l'exemple ci dessus, deux instances Color red vont être instanciées. (provenant des propriétés color et backround-color), alors qu'une seule suffirait.
  • libération des ressources : la libération des ressources (uniquement en SWT) doit être effectuée à l'aide de la méthode dispose() une fois que la ressource n'est plus utilisée.

Pour résoudre ces deux problématiques, le moteur CSS peut utiliser une l'implémentation de org.akrogen.tkui.css.core.resources.IResourcesRegistry qui permet de :

  • gérer les caches des ressources comme Color, Cursor, Font, Image...à l'aide des méthodes getResource , registerResource , unregisterResource .
  • libérer les ressources qui ont été mise en cache à l'aide de la méthode dispose.

Pour enregistrer l'implémentation de IResourcesRegistry à utiliser par le moteur CSS, la méthode setResourcesRegistry doit être appelée.

public void setResourcesRegistry(IResourcesRegistry resourcesRegistry);

Dans le cas de SWT, la classe org.akrogen.tkui.css.swt.resources.SWTResourcesRegistry est utilisée et permet de gérer le cache et la libération des des ressources Color, Cursor, Font, Image. Le constructeur SWTResourcesRegistry attend le paramètre org.eclipse.swt.widgets.Display qui ajoute un listener sur SWT.Dispose.

display.addListener(SWT.Dispose, new Listener() {
	public void handleEvent(Event event) {
		dispose();
	}
});
ce qui permet d'appeler le dispose du IResourcesRegisrty lorsque le Display SWT est libéré.

CSSStylableElement

Pour appliquer la valeur d'une propriété CSS à une widget, le moteur CSS travaille avec une interface unique org.w3c.dom.Element ou plus exactement org.akrogen.tkui.css.core.dom.CSSStylableElement qui hérite de Element. Chacune des widgets (JComponent Swing ou Control SWT) sont wrappés par une instance org.akrogen.tkui.css.core.dom.CSSStylableElement .

Cette solution permet d'une part d'utiliser des factory communes SAC SelectorFactory , et ConditionFactory pour Swing et SWT, mais d'autre part utiliser les interfaces w3c qui utilise des Element w3c. La moteur CSS utilise par exemple en interne une implémentation w3c de ViewCSS (pour calculer les styles CSS d'une widget) qui a une méthode getComputedStyle qui attend en paramètre une instance Element w3c.

CSSStylableElement permet aussi de gérer les informations :

  • nom du selector à l'aide de la méthode getLocalName . Par exemple il est possible d'écrire un style CSS en SWT comme ceci :
    Label {
        color:red;
    }
    En SWT, l'implémentation CSSStylableElement utilisée est org.akrogen.tkui.css.swt.dom.SWTElement et son implémentation de la méthode getLocalName est :
    public String getLocalName() {
       Widget widget = getWidget();
       Class clazz = widget.getClass();
       return ClassUtils.getSimpleName(clazz);
    }
    Ce qui permet d'utiliser les noms des classes SWT en tant que selector.
  • id à l'aide de la méthode getCSSId . Par exemple il est possible d'écrire un style CSS avec un id comme ceci :
    Label#MyId {
        color:red;
    }
    En SWT, l'implémentation CSSStylableElement utilisée est org.akrogen.tkui.css.swt.dom.SWTElement et son implémentation de la méthode getCSSId est :
    public String getCSSId() {
        Widget widget = getWidget();
        Object id = widget.getData("id");
        if (id != null)
          return id.toString();
        return null;
    }
    Ce qui permet de gérer les id des widgets SWT comme ceci :
    Label label = ...
    label.setData("id", "MyId");
  • class à l'aide de la méthode getCSSClass . Par exemple il est possible d'écrire une class CSS comme ceci :
    .redClass {
        color:red;
    }
    En SWT, l'implémentation CSSStylableElement utilisée est org.akrogen.tkui.css.swt.dom.SWTElement et son implémentation de la méthode getCSSClass est :
    public String getCSSClass() {
        Widget widget = getWidget();
        Object id = widget.getData("class");
        if (id != null)
          return id.toString();
        return null;
    }
    Ce qui permet de gérer les class CSS des widgets SWT comme ceci :
    Label label = ...
    label.setData("class", "redClass");
  • style à l'aide de la méthode getCSSStyle . Par exemple il est possible d'afecter un style inline a une widget SWT comme ceci
    Label label = ...
    label.setData("style", "color:red;");
    En SWT, l'implémentation CSSStylableElement utilisée est org.akrogen.tkui.css.swt.dom.SWTElement et son implémentation de la méthode getCSSStyle est :
    public String getCSSClass() {
        Widget widget = getWidget();
        Object id = widget.getData("style");
        if (id != null)
          return id.toString();
        return null;
    }

CSSStylableElement wrappe une widget (SWT ou Swing) et permet de gérer ses informations id, class, style... TK-UI fournit plusieurs implémentation de CSSStylableElement :

IElementProvider

Il est ainsi possible de personnaliser le moteur CSS pour gérer les id, class, style à l'aide d'une implémentation de CSSStylableElement décrit ci dessus. Pour indiquer au moteur CSS l'implémentation CSSStylableElement à utiliser, il faut implémenter l'interface org.akrogen.tkui.css.core.dom.IElementProvider .

public interface IElementProvider {

	/**
	 * Return the w3c Element which wrap the native widget<code>element</code>
	 * (SWT Control, Swing JComponent). The <code>element</code> can be the
	 * w3c Element.
	 * 
	 * @param element
	 * @return
	 */
	public Element getElement(Object element);

}

TK-UI fournit plusieurs implémentation de IElementProvider :

Pour enregistrer l'implémentation de IElementProvider a utiliser par le moteur CSS, la méthode setElementProvider doit être appelée.

public void setElementProvider(IElementProvider elementProvider);

ICSSPropertyHandler

Pour appliquer la valeur d'une propriété CSS à une widget, le moteur CSS utilise une interface ICSSPropertyHandler.

public interface ICSSPropertyHandler {
	/**
	 * Apply CSS Property <code>property</code> (ex : background-color) with
	 * CSSValue <code>value</code> (ex : red) into the <code>element</code>
	 * (ex : Swing Component, SWT Widget).
	 * 
	 * @param element
	 *            Swing Component, SWT Widget...
	 * @param property
	 *            CSS Property
	 * @param value
	 *            CSS value
	 * @param pseudo
	 * @param engine
	 *            CSS Engine
	 * @return
	 * @throws Exception
	 */
	public boolean applyCSSProperty(Object element, String property,
			CSSValue value, String pseudo, CSSEngine engine) throws 
                  Exception;

	/**
	 * Retrieve CSS value (ex : red) of CSS Property <code>property</code> (ex :
	 * background-color) from the <code>element</code> (ex : Swing Component,
	 * SWT Widget).
	 * 
	 * @param element
	 * @param property
	 *            CSS Property
	 * @param engine
	 *            CSS Engine
	 * @return
	 * @throws Exception
	 */
	public String retrieveCSSProperty(Object element, String property,
			CSSEngine engine) throws Exception;
}
Il existe deux méthodes pour enregistrer les propriétés CSS au sein du moteur CSS : en décrivant explicitement le mapping entre le nom de la propriété CSS et le handler à utiliser. par règle de nommage : le nom de la classe Handler correpond au nom de la propriété CSS. Veuillez consultez le guide développeur pour plus d'information (TODO).

ICSSValueConverter

Le moteur CSS stocke en interne des instances CSSValue. Lorsque 'un style doit être appliqué, à une widget SWT ou un Component Swing, l'instance CSSValue doit être converti en instance de l'UI (ex: convertir une CSSValue (red) en objet java.awt.Color Swing). Une implémentation de ICSSValueConverter permet de convertir une instance CSSValue en objet utilisable par l'UI. Voici le code de ICSSValueConverter :

public interface ICSSValueConverter {

	/**
	 * Returns the type to which this converter can convert. The return type is
	 * Object rather than Class to optionally support richer type systems than
	 * the one provided by Java reflection.
	 * 
	 * @return the type to which this converter can convert, or null if this
	 *         converter is untyped
	 */
	public Object getToType();

	/**
	 * Returns the result of the conversion of the given CSSValue
	 * <code>value</code>.
	 * 
	 * @param value
	 *            the CSSValue to convert, of type {@link #getFromType()}
	 * @return the converted object, of type {@link #getToType()}
	 */
	public Object convert(CSSValue value, CSSEngine engine, Object context)
			throws Exception;

	public String convert(Object value, CSSEngine engine, Object context)
			throws Exception;

	public String convert(Object value, CSSEngine engine, Object context,
			ICSSValueConverterConfig config) throws Exception;
}

En appelant la méthode convert de CSSEngine, le moteur CSS vérifie dans un premier temps que la ressource (Swing ou SWT) n'est pas dans le cache IResourcesRegistry avant d'appeler le converter adéquate.