Dans cette exemple, on apprendra à ajouter sa propre propriété CSS tooltip-uppercase qui permettra de mettre en majuscule le texte d'un Tooltip d'un Component Swing. Il sera ainsi possible de mettre en majuscule certain Component Swing à l'aide des Selectors. Plus précisemment cette exemple permettra de mettre en majuscule les textes des tooltip des JLabel. La feuille de style s'ecrira comme ceci :
JLabel { tooltip-uppercase:true; }
JLabel label = new JLabel(); label.setText("Jlabel text"); label.setToolTipText("bla bla bla...");
Il existe deux stratégies pour enregister sa propriétés CSS dans le moteur CSS :
Dans notre exemple on utilisera la première stratégie qui parait plus rébarabative mais qui est plus optimisée, car la deuxième stratégie impose de rechercher la classe CSSPropertyXXXHandler dans le ClassLoader. De plus cette dernière stratégie n'est pas encore bien finalisée concernant la fonctionnalité de réinitialisation des widgets avant d'appliquer un nouveau style CSS (pour pouvoir appliquer des styles CSS au runtime), car ceci impose de retrouver toutes les propriétés CSS enregistrées dans le ClassLoader, fonctionnalité non développée aujourd'hui.
Avant de démarrer le développement d'une nouvelle propriété CSS, vous devez créer un projet (Eclipse, Netbeans...) qui font référence aux libraires jar fournit dans la distribution org.akrogen.tkui.css.swing*
Voici une copie d'écran du projet Eclipse utilisé pour ce tutorial :La première étape est de créer sa classe CSSEngine . Vous pouvez directement utiliser CSSSwingEngineImpl (qui étend AbstractCSSSwingEngineImpl) et qui configure les propriétés CSS2. AbstractCSSSwingEngineImpl permet de configurer Element Provider pour gerer les selectors de type Swing (dans notre cas on ecrit le style avec JLabel {...}). Dans notyre cas nous étendons la classe CSSSwingEngineImpl pour bénéficier des propriétés CSS2 déja implementées.
Pour cela créer la classe MyCSSSwingEngine dans le package com.mycompany.myapp.css.swing.engine :
package com.mycompany.myapp.css.swing.engine; import org.akrogen.tkui.css.swing.engine.CSSSwingEngineImpl; public class MyCSSSwingEngineImpl extends CSSSwingEngineImpl { protected void initializeCSSPropertyHandlers() { super.initializeCSSPropertyHandlers(); } }
Dans cet exemple, nous utilisons la stratégie Stratégie mapping nom propriété/ICSSPropertyHandler ce qui impose de définir une interface qui traite un ensemble de propriété CSS. Dans notre cas nous souhaitons gérer les propriétés CSS concernant les tooltip. Pour cela créer l'interface ICSSPropertyTooltipHandler dans le package com.mycompany.myapp.css.properties qui hérite de l'interface ICSSPropertyHandler . Remarquee que le package utilisé est com.mycompany.myapp.css.properties et pas com.mycompany.myapp.css.swing.properties car cette interface peut aussi être utilisée par une implémentation autre que Swing (comme SWT).
package com.mycompany.myapp.css.properties; import org.akrogen.tkui.css.core.dom.properties.ICSSPropertyHandler; public interface ICSSPropertyTooltipHandler extends ICSSPropertyHandler { }
Il faut ensuite créer l'implementation Swing qui va traiter les propriétés CSS tootip sur des Component Swing, pour cela créer la classe CSSPropertyTooltipHandlerImpl dans le package com.mycompany.myapp.css.swing.properties .
package com.mycompany.myapp.css.swing.properties; import org.akrogen.tkui.css.core.dom.properties.ICSSPropertyHandler; import org.akrogen.tkui.css.core.engine.CSSEngine; import org.w3c.dom.css.CSSValue; import com.mycompany.myapp.css.properties.ICSSPropertyTooltipHandler; public class CSSPropertyTooltipHandlerImpl implements ICSSPropertyTooltipHandler { public static final ICSSPropertyHandler INSTANCE = new CSSPropertyTooltipHandlerImpl(); public boolean applyCSSProperty(Object element, String property, CSSValue value, String pseudo, CSSEngine engine) throws Exception { System.out.println("Apply CSS property [name=" + property + ", value=" + value + "] to the element class=" + element.getClass()); return false; } public String retrieveCSSProperty(Object element, String property, CSSEngine engine) throws Exception { System.out.println("CSSPropertyTooltipHandlerImpl#retrieveCSSProperty"); return null; } }
A cette étape, il faut enregistrer l'implémentation CSSPropertyTooltipHandlerImpl dans notre moteur CSS. Veuillez modifier la classe MyCSSSwingEngineImpl comme ceci :
package com.mycompany.myapp.css.swing.engine; import org.akrogen.tkui.css.swing.engine.CSSSwingEngineImpl; import com.mycompany.myapp.css.properties.ICSSPropertyTooltipHandler; import com.mycompany.myapp.css.swing.properties.CSSPropertyTooltipHandlerImpl; public class MyCSSSwingEngineImpl extends CSSSwingEngineImpl { protected void initializeCSSPropertyHandlers() { super.initializeCSSPropertyHandlers(); super.registerCSSProperty("tooltip-uppercase", ICSSPropertyTooltipHandler.class); super.registerCSSPropertyHandler(ICSSPropertyTooltipHandler.class, CSSPropertyTooltipHandlerImpl.INSTANCE); } }
super.registerCSSProperty("tooltip-uppercase", ICSSPropertyTooltipHandler.class);
super.registerCSSPropertyHandler(ICSSPropertyTooltipHandler.class, CSSPropertyTooltipHandlerImpl.INSTANCE);
A cette étape il est possible de tester si la propriété CSS tooltip-uppercase est enregistré correctement dans le moteur CSS et si elle s'applique bien à un JLabel. Créer la classe de test avec le code suivant :
package com.mycompany.myapp.css.test; import java.io.StringReader; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import org.akrogen.tkui.css.core.engine.CSSEngine; import com.mycompany.myapp.css.swing.engine.MyCSSSwingEngineImpl; public class TestMyCSSSwingEngine { public static void main(String[] args) { try { CSSEngine engine = new MyCSSSwingEngineImpl(); engine.parseStyleSheet(new StringReader("JLabel {tooltip-uppercase:true;}")); /*--- Start UI Swing ---*/ JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JPanel panel = new JPanel(); frame.getContentPane().add(panel); // Label JLabel label = new JLabel(); label.setText("Jlabel text"); label.setToolTipText("bla bla bla..."); panel.add(label); /*--- End UI Swing ---*/ // Apply Styles engine.applyStyles(frame, true); frame.pack(); frame.setVisible(true); } catch (Exception e) { e.printStackTrace(); } } }
Après avoir lancé le test, le texte du tooltip du JLabel n'est pas mis en majuscule mais vous devez voir dans le console le message affiché
Apply CSS property [name=tooltip-uppercase, value=true] to the element class=class org.akrogen.tkui.css.swing.dom.SwingElement
A cette étape CSSPropertyTooltipHandlerImpl est enregistré correctment dans le moteur CSS. Il faut maintenant traiter la mise en majuscule du tooltip.
Le paramètre element de la méthode applyCSSProperty est une instance de SwingElement. Pour mettre en majuscule, il faut récupérer le Component JLabel de SwingElement, pour cela modifier le code de la méthode applyCSSProperty de CSSPropertyTooltipHandlerImpl comme ceci :
public boolean applyCSSProperty(Object element, String property, CSSValue value, String pseudo, CSSEngine engine) throws Exception { Component component = SwingElementHelpers.getComponent(element); if (component != null) { System.out.println("Apply CSS property [name=" + property + ", value=" + value + "] to the component class=" + component.getClass()); return true; } return false; }
Relancer le test, la console doit afficher le message suivant :
Apply CSS property [name=tooltip-uppercase, value=true] to the component class=class javax.swing.JLabel
TK-UI fournit la classe abstraite org.akrogen.tkui.css.swing.properties.AbstractCSSPropertySwingHandler qui permet de récuperer le Component Swing à partir d'un Object element et de gérer le retour de la méthode applyCSSproperty comme décrit ci dessus. Pour utiliser cette classe, modifier la classe CSSPropertyTooltipHandlerImpl comme ceci :
package com.mycompany.myapp.css.swing.properties; import java.awt.Component; import org.akrogen.tkui.css.core.dom.properties.ICSSPropertyHandler; import org.akrogen.tkui.css.core.engine.CSSEngine; import org.akrogen.tkui.css.swing.properties.AbstractCSSPropertySwingHandler; import org.w3c.dom.css.CSSValue; public class CSSPropertyTooltipHandlerImpl extends AbstractCSSPropertySwingHandler { public static final ICSSPropertyHandler INSTANCE = new CSSPropertyTooltipHandlerImpl(); public void applyCSSProperty(Component component, String property, CSSValue value, String pseudo, CSSEngine engine) throws Exception { System.out.println("Apply CSS property [name=" + property + ", value=" + value + "] to the component class=" + component.getClass()); } public String retrieveCSSProperty(Component element, String property, CSSEngine engine) throws Exception { System.out.println("CSSPropertyTooltipHandlerImpl#retrieveCSSProperty"); return null; } }
A cette étape il faut récuperer la valeur true du CSSValue . Pour cela modifier le code de la méthode applyCSSProperty comme ceci :
public void applyCSSProperty(Component component, String property, CSSValue value, String pseudo, CSSEngine engine) throws Exception { boolean isUpperCase = false; if (value.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE) { CSSPrimitiveValue primitiveValue = (CSSPrimitiveValue) value; isUpperCase = "true".equals(primitiveValue.getStringValue()); } if (isUpperCase) { System.out.println("Apply CSS property [name=" + property + ", value=" + value + "] to the component class=" + component.getClass()); } }
A cette étape il est maintenant possible de mettre en majuscule le tooltip du Component JLabel, pour cela modifier le code de la méthode applyCSSProperty de CSSPropertyTooltipHandlerImpl comme ceci :
public void applyCSSProperty(Component component, String property, CSSValue value, String pseudo, CSSEngine engine) throws Exception { boolean isUpperCase = false; if (value.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE) { CSSPrimitiveValue primitiveValue = (CSSPrimitiveValue) value; isUpperCase = "true".equals(primitiveValue.getStringValue()); } if (isUpperCase) { JComponent jComponent = (JComponent)component; String toolTipText = jComponent.getToolTipText(); toolTipText = toolTipText.toUpperCase(); jComponent.setToolTipText(toolTipText); } }
Il est possible d'utiliser un converter pour transformer une instance CSSValue en java.lang.Boolean en implémentant l'interface ICSSValueConverter . Ce concerter permet :
Pour créer un converter, il est conseillé de partir de la classe AbstractCSSValueConverter
qui implémente quelques méthodes dont la méthode getToType
(qui indique le type renvoye du converter, dans notre cas ca sera java.lang.Boolean)
qui est renseignée dans son constructeur.
Créer la classe MyCSSValueBooleanConverterImpl
dans le package
com.mycompany.myapp.css.converters
comme ceci :
package com.mycompany.myapp.css.converters; import org.akrogen.tkui.css.core.dom.properties.converters.AbstractCSSValueConverter; import org.akrogen.tkui.css.core.dom.properties.converters.ICSSValueConverter; import org.akrogen.tkui.css.core.dom.properties.converters.ICSSValueConverterConfig; import org.akrogen.tkui.css.core.engine.CSSEngine; import org.w3c.dom.css.CSSPrimitiveValue; import org.w3c.dom.css.CSSValue; public class MyCSSValueBooleanConverterImpl extends AbstractCSSValueConverter { public static final ICSSValueConverter INSTANCE = new MyCSSValueBooleanConverterImpl(); public MyCSSValueBooleanConverterImpl() { super(Boolean.class); } public Object convert(CSSValue value, CSSEngine engine, Object context) throws Exception { if (value.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE) { CSSPrimitiveValue primitiveValue = (CSSPrimitiveValue) value; if ("true".equals(primitiveValue.getStringValue())) return Boolean.TRUE; } return Boolean.FALSE; } public String convert(Object value, CSSEngine engine, Object context, ICSSValueConverterConfig config) throws Exception { if (value instanceof Boolean) { Boolean b = (Boolean) value; if (b.booleanValue()) return "true"; } return "false"; } }
Pour utiliser cette classe converter vous pouvez l'utiliser directement dans la méthode applyCSSProperty comme ceci :
public void applyCSSProperty(Component component, String property, CSSValue value, String pseudo, CSSEngine engine) throws Exception { Boolean isUpperCase = (Boolean)MyCSSValueBooleanConverterImpl.INSTANCE.convert(value, engine, null); if (isUpperCase.booleanValue()) { JComponent jComponent = (JComponent)component; String toolTipText = jComponent.getToolTipText(); toolTipText = toolTipText.toUpperCase(); jComponent.setToolTipText(toolTipText); } }
Il est cependant conseillé d'utiliser les converters en appelant la méthode convert du moteur CSS qui permet de rechercher dans un premier temps la valeur converti dans un cache (voir IResourcesRegistry ) avant d'appeler le converter adéquate.
Dans notre cas, cette mise en cache n'est pas très pertinente mais lorsque l'on souhaite manipulee des ressources comme Color, Font, la méthode convert de engien vérifie dans un premier temps dans le cache si une ressource a deja ete creée avec les valeurs de l'instance CSSValue avant d'appeler le converter qui lui creé une nouvelle instance d'une resource.
Pour utiliser notre converter à partir du CSSEngine, il faut l'enregistrer à l'aide de la methode registerCSSValueConverter comme ceci :
package com.mycompany.myapp.css.swing.engine; import org.akrogen.tkui.css.swing.engine.CSSSwingEngineImpl; import com.mycompany.myapp.css.converters.MyCSSValueBooleanConverterImpl; import com.mycompany.myapp.css.properties.ICSSPropertyTooltipHandler; import com.mycompany.myapp.css.swing.properties.CSSPropertyTooltipHandlerImpl; public class MyCSSSwingEngineImpl extends CSSSwingEngineImpl { public MyCSSSwingEngineImpl() { super(); super.registerCSSValueConverter(MyCSSValueBooleanConverterImpl.INSTANCE); } protected void initializeCSSPropertyHandlers() { super.initializeCSSPropertyHandlers(); super.registerCSSProperty("tooltip-uppercase", ICSSPropertyTooltipHandler.class); super.registerCSSPropertyHandler(ICSSPropertyTooltipHandler.class, CSSPropertyTooltipHandlerImpl.INSTANCE); } }
Vous pouvez ensuite appeler dans la méthode applyCSSProperty la méthode convert du CSS engine comme ceci :
public void applyCSSProperty(Component component, String property, CSSValue value, String pseudo, CSSEngine engine) throws Exception { Boolean isUpperCase = (Boolean)engine.convert(value, Boolean.class, null); if (isUpperCase.booleanValue()) { JComponent jComponent = (JComponent)component; String toolTipText = jComponent.getToolTipText(); toolTipText = toolTipText.toUpperCase(); jComponent.setToolTipText(toolTipText); } }
DEBUG [main] (SwingResourcesRegistry.java:59) - Cache Resource key=true