CSS properties/tooltip-content

Avant de lire cette section, veuillez lire la section tooltip-uppercase . Cette section permet de gérer une nouvelle propriété CSS tooltip-content qui permet de gérer le contenu des ToolTips des Component Swing avec le style CSS suivant :

JLabel {
    tooltip-content:'bla bla bla content';	
}
Le code JLabel suivant
JLabel label = new JLabel();
label.setText("Jlabel text");
label.setToolTipText("bla bla bla...");

affiche l'interface suivante apres avoir appliqué le style CSS tooltip-content:'bla bla bla content' :

La deuxième partie de cette section expliquera comment gérer les 2 propriétés CSS dans la même règle CSS comme ceci :
JLabel {
    tooltip-uppercase:true;
    tooltip-content:'bla bla bla content';	
}
Le code JLabel suivant
JLabel label = new JLabel();
label.setText("Jlabel text");
label.setToolTipText("bla bla bla...");
affiche l'interface suivante apres avoir appliqué le style CSS tooltip-uppercase:true;tooltip-content:'bla bla bla content' :

ICSSPropertyTooltipHandler/Multi CSS property

ICSSPropertyTooltipHandler traite plusieurs propriétés CSS. Il faut donc à un moment appliquer le bon code en fonction du nom de la propriété CSS. Dans la suite des examples, on souhaite être générique et pouvoir bénéficier du maximum de code pour une autre implémentation (SWT par exemple). Les exemples suivants suivent le même modèle que les implémentation des propriétés CSS2 de TK-UI, mais vous n'etes pas obligé d'effectuer ces modifications si vous souhaitez uniquement implémenter le moteur CSS Swing. Vous pouvez effectuer par exemple le test de la propriété comme ceci :

public void applyCSSProperty(Component component, String property,
            CSSValue value, String pseudo, CSSEngine engine) throws Exception {
    if ("tooltip-uppercase".equals(property)) {
        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);
        }
        return;
    }
    if ("tooltip-content".equals(property)) {
       // Manage tooltip-content....
      return;
    }
}

ICSSPropertyTooltipHandler Interface

Veuillez modifier l'interface ICSSPropertyTooltipHandler qui ajoute les méthodes applyCSSProperty* et retrieveCSSProperty* pour les 2 nouvelles propriétés CSS tooltip-uppercase et tooltip-content .

package com.mycompany.myapp.css.properties;

import org.akrogen.tkui.css.core.dom.properties.ICSSPropertyHandler;
import org.akrogen.tkui.css.core.engine.CSSEngine;
import org.w3c.dom.css.CSSValue;

public interface ICSSPropertyTooltipHandler extends ICSSPropertyHandler {

	public void applyCSSPropertyTooltipUpperCase(Object element, String property,
			CSSValue value, String pseudo, CSSEngine engine) throws Exception;

	public void applyCSSPropertyTooltipContent(Object element, String property,
			CSSValue value, String pseudo, CSSEngine engine) throws Exception;
	
	public String retrieveCSSPropertyTooltipContent(Object element,
			String property, CSSEngine engine) throws Exception;
	
	public String retrieveCSSPropertyTooltipUpperCase(Object element,
			String property, CSSEngine engine) throws Exception;
}

AbstractCSSPropertyTooltipHandler

Veuillez créer la classe abtraire AbstractCSSPropertyTooltipHandler dans le package com.mycompany.myapp.css.properties qui permet de router sur le bonne méthode applyCSSProperty(*) et retrieveCSSProperty(*) en fonction du nom de la propriété CSS. Cette classe est indépendante du moteur Swing et peut être utilisé dans une autre implémentation (comme SWT).

package com.mycompany.myapp.css.properties;

import org.akrogen.tkui.css.core.engine.CSSEngine;
import org.w3c.dom.css.CSSValue;

public abstract class AbstractCSSPropertyTooltipHandler implements
		ICSSPropertyTooltipHandler {

    public boolean applyCSSProperty(Object element, String property,
            CSSValue value, String pseudo, CSSEngine engine) throws Exception {
        if ("tooltip-uppercase".equals(property)) {
            applyCSSPropertyTooltipUpperCase(element, property, value, pseudo,
                    engine);
        }
        if ("tooltip-content".equals(property)) {
            applyCSSPropertyTooltipContent(element, property, value, pseudo,
                    engine);
        }
        return false;
    }

    public String retrieveCSSProperty(Object element, String property,
            CSSEngine engine) throws Exception {
        if ("tooltip-uppercase".equals(property)) {
            return retrieveCSSPropertyTooltipUpperCase(element, property,
                    engine);
        }
        if ("tooltip-content".equals(property)) {
            return retrieveCSSPropertyTooltipContent(element, property, engine);
        }
        return null;
    }
}

CSSPropertyTooltipHandlerImpl

Modifier la classe CSSPropertyTooltipHandlerImpl comme ceci pour implémenter AbstractCSSPropertyTooltipHandler et gérer la propriété CSS tooltip-content dans la méthode applyCSSPropertyTooltipContent :
package com.mycompany.myapp.css.swing.properties;

import javax.swing.JComponent;

import org.akrogen.tkui.css.core.dom.properties.ICSSPropertyHandler;
import org.akrogen.tkui.css.core.engine.CSSEngine;
import org.akrogen.tkui.css.swing.helpers.SwingElementHelpers;
import org.w3c.dom.css.CSSPrimitiveValue;
import org.w3c.dom.css.CSSValue;

import com.mycompany.myapp.css.properties.AbstractCSSPropertyTooltipHandler;

public class CSSPropertyTooltipHandlerImpl extends
        AbstractCSSPropertyTooltipHandler {

    public static final ICSSPropertyHandler INSTANCE = new CSSPropertyTooltipHandlerImpl();

    public boolean applyCSSProperty(Object element, String property,
            CSSValue value, String pseudo, CSSEngine engine) throws Exception {
        JComponent component = (JComponent) SwingElementHelpers
                .getComponent(element);
        if (component == null)
            return false;
        super.applyCSSProperty(component, property, value, pseudo,
                engine);
        return true;
    }

    public String retrieveCSSProperty(Object element, String property,
            CSSEngine engine) throws Exception {
        JComponent component = (JComponent) SwingElementHelpers
                .getComponent(element);
        if (component == null)
            return null;
        return super.retrieveCSSProperty(component, property, engine);
    }

    public void applyCSSPropertyTooltipUpperCase(Object element,
            String property, CSSValue value, String pseudo, CSSEngine engine)
            throws Exception {
        JComponent component = (JComponent) element;
        Boolean isUpperCase = (Boolean) engine.convert(value, Boolean.class,
                null);
        if (isUpperCase.booleanValue()) {
            String toolTipText = component.getToolTipText();
            toolTipText = toolTipText.toUpperCase();
            component.setToolTipText(toolTipText);
        }
    }

    public void applyCSSPropertyTooltipContent(Object element, String property,
            CSSValue value, String pseudo, CSSEngine engine) throws Exception {
        JComponent component = (JComponent) element;
        if (value.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
            CSSPrimitiveValue primitiveValue = (CSSPrimitiveValue) value;
            String tooltipText = primitiveValue.getStringValue();
            component.setToolTipText(tooltipText);
        }
    }

    public String retrieveCSSPropertyTooltipContent(Object element,
            String property, CSSEngine engine) throws Exception {
        System.out
                .println("CSSPropertyTooltipHandlerImpl#retrieveCSSPropertyTooltipContent");
        return null;
    }

    public String retrieveCSSPropertyTooltipUpperCase(Object element,
            String property, CSSEngine engine) throws Exception {
        System.out
                .println("CSSPropertyTooltipHandlerImpl#retrieveCSSPropertyTooltipUpperCase");
        return null;
    }

}
Voici les explication de ce code :
  • la méthode applyCSSProperty a le code suivant :
    public boolean applyCSSProperty(Object element, String property,
                CSSValue value, String pseudo, CSSEngine engine) throws Exception {
        JComponent component = (JComponent) SwingElementHelpers
                .getComponent(element);
        if (component == null)
            return false;
        super.applyCSSProperty(component, property, value, pseudo,
                engine);
        return true;
    }
    Cette méthode récupére le Component Swing en fonction du paramètre element. Si le Component Swing est null, la méthode retourne false. Dans le cas contraire elle appelle super.applyCSSProperty en passant le Component Swing en paramètre. En fin de la méthode, celle ci retourn etrue pour indiquer au moteur CSS que la propriété peut être appliqué a l'objet element.
  • la méthode applyCSSPropertyTooltipUpperCase est appelée lorsque la propriété à le nom tooltip-uppercase . Son implémentation suit le code de la section tooltip-uppercase .
  • la méthode applyCSSPropertyTooltipContent est appelée lorsque la propriété à le nom tooltip-content . Voici son implémentation :
    public void applyCSSPropertyTooltipContent(Object element, String property,
            CSSValue value, String pseudo, CSSEngine engine) throws Exception {
        JComponent component = (JComponent) element;
        if (value.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
            CSSPrimitiveValue primitiveValue = (CSSPrimitiveValue) value;
            String tooltipText = primitiveValue.getStringValue();
            component.setToolTipText(tooltipText);
        }
    }
    Elle récupère la string du CSSValue et met à jour le tooltiptext du Component Swing.

Register ICSSPropertyHandler

A cette étape, il faut indiquer au moteur CSS que la propriété CSS tooltip-content est traitée par l'interface ICSSPropertyTooltipHandler comme comme ce qui a été fait pour la propriété tooltip_uppercase , à l'aide de la méthode registerCSSProperty . 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.registerCSSProperty("tooltip-content", ICSSPropertyTooltipHandler.class);		
		super.registerCSSPropertyHandler(ICSSPropertyTooltipHandler.class,
				CSSPropertyTooltipHandlerImpl.INSTANCE);
	}
}

CSSEngine Test

A cette étape il est possible de tester si la propriété CSS tooltip-content est enregistré correctement dans le moteur CSS et si elle s'applique bien à un JLabel. Créer la classe de test TestMyCSSSwingEngine 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-content:'bla bla bla content';}"));

             /*---  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 doit avoir le contenu bla bla bla content .

CSSElementContext,CSSTooltipProperties, ICSSPropertyHandler2

Dans cette partie on souhaite combiner les deux propriété CSS tooltip-uppercase et tooltip-content dans la même règle CSS. Faisons un premier test en écrivant le style CSS suivant :

JLabel {
    tooltip-content:'bla bla bla content';
    tooltip-uppercase:true;    
}
Pour cela, modifier la classe TestMyCSSSwingEngine avec ce code et relancer le test :
engine.parseStyleSheet(new StringReader(
    "JLabel {tooltip-content:'bla bla bla content';tooltip-uppercase:true;}"));
Le tooltip du JLabel a bien la valeur attendu, autrement dit BLA BLA BLA CONTENT . Cependant si vous écrivez le style CSS suivant dans cet ordre :
JLabel {    
    tooltip-uppercase:true;
    tooltip-content:'bla bla bla content';	
}
Pour cela, modifier la classe TestMyCSSSwingEngine avec ce code et relancer le test :
engine.parseStyleSheet(new StringReader(
    "JLabel {tooltip-uppercase:true;tooltip-content:'bla bla bla content';}"));
Le tooltip du JLabel a la valeur bla bla bla content , ce qui n'est pas la valeur attendu. Ce problème s'explique par le fait que la première propriété CSS tooltip-uppercase est appliqué avant tooltip-content . L'application de tooltip-content écrase celle de tooltip-uppercase .

Pour régler ce problème, il est impossible d'appliquer les 2 propriétés CSS tooltip-uppercase et tooltip-content directement sur les COmponent Swing car on ne maîtrise par l'ordre de ces propriétés. Pour résoudre, ce problème, il faut

  • utiliser une instance CSSTooltipProperties transitoire auquel on applique tous les styles CSS (tooltip-uppercase et tooltip-content )
  • Cette instance CSSTooltipProperties est stockée dans le contexte org.akrogen.tkui.css.core.engine.CSSElementContext associé à l'instance element qui est récupé avec le code suivant :
    CSSElementContext context = engine.getCSSElementContext(element);
    Cette instance est stocké avec la clé CSSTooltipProperties comme ceci :
    CSSTooltipProperties tooltipProperties = (CSSTooltipProperties) context
    				.getData("CSSTooltipProperties");
    if (tooltipProperties == null) {
        tooltipProperties = new CSSTooltipProperties();
        context.setData("CSSTooltipProperties", tooltipProperties);
    }
    et peut être retrouvé comme ceci :
    CSSElementContext context = engine.getCSSElementContext(element);
    CSSTooltipProperties tooltipProperties = (CSSTooltipProperties) context.
        getData("CSSTooltipProperties");
  • Lorsque toutes les propriétés CSS ont été appliqué sur l'instance CSSTooltipProperties , on utilise cette instance pour modifier le tooltiptext du Component Swing. La classe CSSPropertyTooltipHandlerImpl doit implémenter l'interface org.akrogen.tkui.css.core.dom.properties.ICSSPropertyHandler2 qui impose à implémenter la méthode onAllCSSPropertiesApplyed qui est appelée lorsque toutes les propriétés CSS ont été appliqué.

CSSTooltipProperties

Créer la classe CSSTooltipProperties dans le package com.mycompany.myapp.css.properties :

package com.mycompany.myapp.css.properties;

public class CSSTooltipProperties {

	private boolean uppercase = false;
	private String content;

	public boolean isUppercase() {
		return uppercase;
	}

	public void setUppercase(boolean uppercase) {
		this.uppercase = uppercase;
	}

	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}
}

CSSPropertyTooltipHandlerImpl

Modifier la classe CSSPropertyTooltipHandlerImpl :

package com.mycompany.myapp.css.swing.properties;

import javax.swing.JComponent;

import org.akrogen.tkui.css.core.dom.properties.ICSSPropertyHandler;
import org.akrogen.tkui.css.core.dom.properties.ICSSPropertyHandler2;
import org.akrogen.tkui.css.core.engine.CSSElementContext;
import org.akrogen.tkui.css.core.engine.CSSEngine;
import org.akrogen.tkui.css.swing.helpers.SwingElementHelpers;
import org.w3c.dom.css.CSSPrimitiveValue;
import org.w3c.dom.css.CSSValue;

import com.mycompany.myapp.css.properties.AbstractCSSPropertyTooltipHandler;
import com.mycompany.myapp.css.properties.CSSTooltipProperties;

public class CSSPropertyTooltipHandlerImpl extends
        AbstractCSSPropertyTooltipHandler implements ICSSPropertyHandler2 {

    public static final ICSSPropertyHandler INSTANCE = new CSSPropertyTooltipHandlerImpl();

    public boolean applyCSSProperty(Object element, String property,
            CSSValue value, String pseudo, CSSEngine engine) throws Exception {
        JComponent component = (JComponent) SwingElementHelpers
                .getComponent(element);
        if (component != null) {
            CSSElementContext context = engine.getCSSElementContext(element);
            CSSTooltipProperties tooltipProperties = (CSSTooltipProperties) context
                    .getData("CSSTooltipProperties");
            if (tooltipProperties == null) {
                tooltipProperties = new CSSTooltipProperties();
                context.setData("CSSTooltipProperties", tooltipProperties);
            }
            super.applyCSSProperty(tooltipProperties, property, value, pseudo,
                    engine);
            return true;
        } else {
            if (element instanceof CSSTooltipProperties) {
                super
                        .applyCSSProperty(element, property, value, pseudo,
                                engine);
                return true;
            }
        }
        return false;
    }

    public String retrieveCSSProperty(Object element, String property,
            CSSEngine engine) throws Exception {
        JComponent component = (JComponent) SwingElementHelpers
                .getComponent(element);
        if (component == null)
            return null;
        return super.retrieveCSSProperty(component, property, engine);
    }

    public void applyCSSPropertyTooltipUpperCase(Object element,
            String property, CSSValue value, String pseudo, CSSEngine engine)
            throws Exception {
        CSSTooltipProperties tooltipProperties = (CSSTooltipProperties) element;
        Boolean isUpperCase = (Boolean) engine.convert(value, Boolean.class,
                null);
        tooltipProperties.setUppercase(isUpperCase.booleanValue());
    }

    public void applyCSSPropertyTooltipContent(Object element, String property,
            CSSValue value, String pseudo, CSSEngine engine) throws Exception {
        CSSTooltipProperties tooltipProperties = (CSSTooltipProperties) element;
        if (value.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE) {
            CSSPrimitiveValue primitiveValue = (CSSPrimitiveValue) value;
            String tooltipText = primitiveValue.getStringValue();
            tooltipProperties.setContent(tooltipText);
        }
    }

    public String retrieveCSSPropertyTooltipContent(Object element,
            String property, CSSEngine engine) throws Exception {
        System.out
                .println("CSSPropertyTooltipHandlerImpl#retrieveCSSPropertyTooltipContent");
        return null;
    }

    public String retrieveCSSPropertyTooltipUpperCase(Object element,
            String property, CSSEngine engine) throws Exception {
        System.out
                .println("CSSPropertyTooltipHandlerImpl#retrieveCSSPropertyTooltipUpperCase");
        return null;
    }
    
    public void onAllCSSPropertiesApplyed(Object element, CSSEngine engine)
            throws Exception {
        JComponent component = (JComponent) SwingElementHelpers
        .getComponent(element);
        CSSElementContext context = engine.getCSSElementContext(element);
        CSSTooltipProperties tooltipProperties = (CSSTooltipProperties) context
                .getData("CSSTooltipProperties");
        if(tooltipProperties != null) {
            String content = tooltipProperties.getContent();
            if(content == null)
                content = component.getToolTipText();
            if(content != null) {
                if (tooltipProperties.isUppercase())
                    content = content.toUpperCase();
            }
            component.setToolTipText(content);
        }        
    }
}

Vous pouvez relancer le test.