- Loading...
...
Say a contributor wished to develop a Watermark control which could overlay a region with copyright information. The Watermark API has a DoubleProperty that controls then angle at which the copyright text is displayed. The contributor wishes to make this copyrightAngle styleable through CSS. The contributor also wants to be able to modify some visual aspect of the Watermark once the copyright has been viewed; perhaps making the Watermark more transparent. The contributor wants to use a 'viewed' pseudpseudo-class state to achieve this.
...
First, the copyrightAngle is made styleable by having it extend one of the StyleableProperty classes
; in this case, StyleableDoubleProperty
. The code shown here follows the typical pattern. Notice that the only difference between a DoubleProperty
and the StyleableDoubleProperty
is the addition of the getCssMetaData
method which links the copyrightAngle property to its corresponding CssMetaData
instance.
Code Block |
---|
private DoubleProperty copyrightAngle = new StyleableDoubleProperty(45d) {
/** Link this property with its CssMetaData */
@Override
public CssMetaData getCssMetaData() {
return COPYRIGHT_ANGLE;
}
@Override
public Object getBean() {
return Node.this;
}
@Override
public String getName() {
return "copyrightAngle";
}
};
public final void setCopyrightAngle(double value) {
copyrightAngle.set(value);
}
public final double getCopyrightAngle() {
return copyrightAngle.get();
}
public final DoubleProperty copyrightAngleProperty() {
return copyrightAngle;
}
|
The CssMetaData
instance provides information about the CSS style and some methods that allow CSS to set the property's value. The convention is to instantiate the CssMetaData
as a singleton (note that this example code is not thread safe, but is sufficient). Here, the CSS property name is "-my-copyright-angle" and the initial value of 45 degrees with the default value of the copyrightAngle property.
Code Block |
---|
private static final CssMetaData<Watermark,Number> COPYRIGHT_ANGLE = new CssMetaData("-my-copyright-angle", 45d) {
public abstract boolean isSettable(Watermark node) {
return copyrightAngle == null || copyrightAngle.isBound() == false;
}
public abstract WritableValue<Number> getWritableValue(Watermark node) {
return copyrightAngleProperty();
}
};
|
In order for the CSS engine to know about the styleable properties of Watermark, the methods getClassCssMetaData()
and getCssMetaData()
need to be implemented. The first is a static method that returns the CssMetaData
of the Watermark class and of its super class; in Watermark's case, the super class is Control
. The second method returns the same value but is implemented as an instance method so that it is not necessary to use reflection to call getClassCssMetaData()
. These methods are called quite frequently; for efficiency, the List<CssMetaData> is only created once. The following code shows a typical implementation.
Code Block |
---|
private static final List<CssMetaData> CSS_META_DATA;
static {
final List<CssMetaData> metaData = new ArrayList<CssMetaData>(Control.getClassCssMetaData());
Collections.addAll(metaData,
COPYRIGHT_ANGLE
);
CSS_META_DATA = Collections.unmodifiableList(metaData);
}
public static List<CssMetaData> getClassCssMetaData() {
return CSS_META_DATA;
}
@Override public List<CssMetaData> getCssMetaData() {
return getClassCssMetaData();
}
|
...
The 'viewed' pseudo-class will be implemented as a BooleanProperty. Although BooleanProperty is most commonly used for pseudo-class state, any property type can be a pseudo-class, even a Styleable*Property. When the 'viewed' property changes value, the code needs to notify CSS that the state has changed. The place to do this is in the invalidated()
method of the property which calls the pseudoClassStateChanged
method, passing it then PseudoClass.State
of the pseudo-class that changed state. Again, this code follows the typical pattern of implementing a property. Note that a "simple" property could be used here, but the invalidated method would still need to be overridden. Since the anonymous class will be created anyway, the few bytes needed for the bean and name will be saved by using BooleanPropertyBase instead of SimpleBooleanProperty.
Code Block |
---|
public final void setViewed(boolean value) {
viewed.set(value);
}
public final boolean isViewed() {
return viewed.get();
}
public final BooleanProperty viewedProperty() {
return viewed;
}
private static final PseudoClass.State VIEWED_PSEUDO_CLASS = PseudoClass.getState("viewed");
private BooleanProperty viewed = new BooleanPropertyBase(false) {
@Override
protected void invalidated() {
pseudoClassStateChanged(VIEWED_PSEUDO_CLASS);
}
@Override
public Object getBean() {
return Node.this;
}
@Override
public String getName() {
return "viewed";
}
};
|
...