• Home
    • View
    • Login
    This page
    • Normal
    • Export PDF
    • Page Information

    Loading...
  1. Dashboard
  2. Undefined Space
  3. OpenJFX
  4. CSS API to support custom UI Controls

Page History

Versions Compared

Old Version 7

changes.mady.by.user J. Duke

Saved on Dec 06, 2012

compared with

New Version 8

changes.mady.by.user J. Duke

Saved on Dec 06, 2012

  • Previous Change: Difference between versions 6 and 7
  • Next Change: Difference between versions 8 and 9
  • View Page History

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Introduction

CSS is an integral part of JavaFX but heretofore has been implemented exclusively in private API within, primarily, the

...

com.sun.javafx.css

...

package.

...

Adding

...

a

...

styleable

...

property

...

in

...

JavaFX

...

is

...

fairly

...

straight-forward

...

and

...

exposing

...

certain

...

elements

...

of

...

the

...

internal

...

API

...

will

...

facilitate

...

the

...

development

...

of

...

custom

...

UI

...

controls

...

in

...

the

...

open

...

source

...

environment.

...

This

...

document

...

outlines

...

this

...

public

...

API.

...

Design

...

Goals

...

The

...

primary

...

goal

...

is

...

to

...

create

...

an

...

API

...

that

...

allows

...

CSS

...

styling

...

to

...

be

...

applied

...

to

...

a

...

JavaFX

...

property.

...

The

...

API

...

should

...

support

...

the

...

following

...

javafx.beans.property

...

types:

...

BooleanProperty,

...

FloatProperty,

...

DoubleProperty,

...

IntegerProperty,

...

LongProperty,

...

and

...

StringProperty.

...

Support

...

for

...

ObjectProperty

...

is

...

desired

...

but

...

would,

...

possibly,

...

require

...

additional

...

hooks

...

into

...

the

...

parser

...

or

...

public

...

API

...

to

...

convert

...

a

...

parsed

...

value

...

to

...

the

...

parameterized

...

type

...

which

...

may

...

not

...

be

...

feasible

...

at

...

this

...

time.

...

However,

...

the

...

ability

...

to

...

style

...

an

...

ObjectProperty

...

with

...

a

...

parameterized

...

type

...

such

...

as

...

Insets

...

or

...

Paint

...

is

...

essential.

...


A

...

secondary

...

goal

...

is

...

to

...

make

...

the

...

API

...

such

...

that

...

an

...

IDEs

...

whould

...

be

...

able

...

to

...

automatically

...

generate

...

much

...

of

...

the

...

necessary

...

code.

...

It

...

is

...

not

...

a

...

goal,

...

however,

...

to

...

create

...

the

...

IDE

...

boilerplate.

...


While

...

it

...

is

...

possible

...

to

...

have

...

classes

...

other

...

than

...

instances

...

of

...

Node

...

be

...

styleable

...

through

...

CSS,

...

this

...

support

...

requires

...

an

...

additional

...

interface

...

which

...

exists

...

in

...

private

...

implementation.

...

Including

...

this

...

interface

...

in

...

the

...

public

...

API

...

is

...

not

...

a

...

goal.

...


The

...

current

...

implementation

...

of

...

CSS

...

in

...

JavaFX

...

is

...

only

...

a

...

small

...

portion

...

of

...

the

...

W3C

...

standards

...

and

...

is

...

only

...

partially

...

compliant

...

with

...

those

...

standards.

...

It

...

is

...

beyond

...

the

...

scope

...

of

...

this

...

API

...

implementation

...

to

...

rectify

...

differences

...

between

...

the

...

W3C

...

standards

...

and

...

the

...

current

...

JavaFX

...

implementation.

...

Use

...

Case

...

Say

...

a

...

contributor

...

wished

...

to

...

develop

...

a

...

Watermark

...

control

...

which

...

could

...

overlay

...

a

...

region

...

and

...

present

...

a

...

widget

...

for

...

obtaining

...

copyright

...

information.

...

The

...

Watermark

...

API

...

has

...

a

...

StringProperty

...

cornerProperty

...

that

...

controls

...

which

...

corner

...

of

...

the

...

region

...

the

...

widget

...

is

...

to

...

be

...

displayed.

...

The

...

contributor

...

wishes

...

to

...

make

...

this

...

cornerProperty

...

styleable

...

through

...

CSS.

Architecture

There are two main pieces to the architecture. First is a StyleablePropertyMetaData whose value can be represented syntactically in a .css file. A StyleablePropertyMetaData encapsulates the CSS property name, the type into which the string value is converted, and the default value of the property. Second is the JavaFX property to which the parsed StyleablePropertyMetaData value applies. Any JavaFX property that supports this styling is a StyleableProperty. A StyleableProperty also incorporates additional logic to ensure that values set by the user through calls to set methods are not overridden by styles in a user agent stylesheet.
There is a one-to-one correspondence between a StyleablePropertyMetaData and a StyleableProperty. A StyleablePropertyMetaData is scoped to a class whereas a StyleableProperty is an attribute of a class instance. Typically, a node will assume the CssMetaData of its ancestors. During CSS processing, the CSS engine iterates over a List<StyleablePropertyMetaData> (particular to the node) and looks up the parsed value of each StyleablePropertyMetaData in turn. If the StyleablePropertyMetaData has a parsed value, the parsed value is converted to the type of the StyleableProperty and the StyleableProperty is set.
Making a property styleable, then, consist of:

  1. defining the javafx.beans.property as a StyleableProperty
  2. creating a corresponding StyleablePropertyMetaData
  3. ensuring the StyleablePropertyMetaData is returned in the List<StyleablePropertyMetaData>

API

StyleableProperty

StyleableProperty is an interface that is implemented by various classes that extend from javafx.beans.property properties; for example,

Code Block


h2. Architecture

There are two main pieces to the architecture. First is a StyleablePropertyMetaData whose value can be represented syntactically in a .css file. A StyleablePropertyMetaData encapsulates the CSS property name, the type into which the string value is converted, and the default value of the property. Second is the JavaFX property to which the parsed StyleablePropertyMetaData value applies. Any JavaFX property that supports this styling is a StyleableProperty. A StyleableProperty also incorporates additional logic to ensure that values set by the user through calls to set methods are not overridden by styles in a user agent stylesheet.
There is a one-to-one correspondence between a StyleablePropertyMetaData and a StyleableProperty. A StyleablePropertyMetaData is scoped to a class whereas a StyleableProperty is an attribute of a class instance. Typically, a node will assume the CssMetaData of its ancestors. During CSS processing, the CSS engine iterates over a List<StyleablePropertyMetaData> (particular to the node) and looks up the parsed value of each StyleablePropertyMetaData in turn. If the StyleablePropertyMetaData has a parsed value, the parsed value is converted to the type of the StyleableProperty and the StyleableProperty is set.
Making a property styleable, then, consist of:
# defining the javafx.beans.property as a StyleableProperty
# creating a corresponding StyleablePropertyMetaData
# ensuring the StyleablePropertyMetaData is returned in the List<StyleablePropertyMetaData>

h2. API
h3. StyleableProperty
StyleableProperty is an interface that is implemented by various classes that extend from javafx.beans.property properties; for example,{code}
public class StyleableBooleanProperty extends BooleanPropertyBase implements StyleableProperty&lt;Boolean&gt;StyleableProperty<Boolean> { ... }{code}.
{code:title=

.

Code Block
titleStyleableProperty.java
}
interface StyleableProperty&lt;T&gt;StyleableProperty<T> {

    /**
     * This method is called from CSS code to set the value of the property.;
     */
    void applyStyle(Origin origin, T value);

    /**
     * Tells the origin of the value of the property. This is needed to
     * determine whether or not CSS can override the value.
     */
    Origin getOrigin();

    /**
     * Reflect back the StyleablePropertyMetaData that corresponds to this;
     * &lt;code&gt;javafx<code>javafx.beans.property.StyleableProperty&lt;/code&gt;StyleableProperty</code>
     */
    StyleablePropertyMetaData getStyleablePropertyMetaData();

}
{code}

In

...

implementation,

...

the

...

applyStyle

...

method

...

delegates

...

to

...

the

...

set

...

method

...

of

...

the

...

javafx.beans.property

...

property

...

.

...

The

...

getStyleablePropertyMetaData

...

method

...

is

...

useful

...

for

...

getting

...

from

...

a

...

StyleableProperty

...

to

...

the

...

corresponding

...

StyleablePropertyMetaData

...

,

...

which

...

is

...

useful

...

for

...

tooling

...

and

...

unit

...

testing.

...

Example:

{
Code Block
}
  private StringProperty corner = new StyleableStringProperty(CORNER, "lower-right") {
    // implementation of StringProperty abstract methods assumed
  }
  public void setCorner(String corner) {
    cornerProperty.set(corner);
  }
  public String getCorner() {
    return cornerProperty().get();
  }
  public final StringProperty cornerProperty() {
    return corner;
  }
{code}

Implementing

...

classes:

...


public

...

class

...

StyleableBooleanProperty

...

extends

...

BooleanPropertyBase

...

implements

...

StyleableProperty<Boolean>

...


public

...

class

...

StyleableFloatProperty

...

extends

...

FloatPropertyBase

...

implements

...

StyleableProperty<Float>

...


public

...

class

...

StyleableDoubleProperty

...

extends

...

DoublePropertyBase

...

implements

...

StyleableProperty<Double>

...


public

...

class

...

StyleableIntegerProperty

...

extends

...

IntegerPropertyBase

...

implements

...

StyleableProperty<Integer>

...


public

...

class

...

StyleableLongProperty

...

extends

...

LongPropertyBase

...

implements

...

StyleableProperty<Long>

...


public

...

class

...

StyleableStringProperty

...

extends

...

StringPropertyBase

...

implements StyleableProperty<String>
public class StyleableObjectProperty<T> extends ObjectPropertyBase<T> implements StyleableProperty<T>

Each of these classes have a constructor taking a StyleablePropertyMetaData arg and another taking a StyleablePropertyMetaData arg and an initial value. For example, StyleableBooleanProperty has the following constructors:

Code Block

   StyleableProperty<String>}}
{{public class StyleableObjectProperty<T> extends ObjectPropertyBase<T> implements StyleableProperty<T>}}

Each of these classes have a constructor taking a {{StyleablePropertyMetaData}} arg and another taking a {{StyleablePropertyMetaData}} arg and an initial value. For example, {{StyleableBooleanProperty}} has the following constructors:
{code}
/**
  * The constructor of the {@code StyleableBooleanProperty}.
  */
  public StyleableBooleanProperty(StyleablePropertyMetaData StyleablePropertyMetaData) {
    super();
    this.StyleablePropertyMetaData = StyleablePropertyMetaData;
  }

  /**
  * The constructor of the {@code StyleableBooleanProperty}.
  *
  * @param StyleablePropertyMetaData
  *      the {@code StyleablePropertyMetaData} that corresponds to this {@code StyleableProperty}
  * @param initialValue
  *      the initial value of the wrapped {@code Object}
  */
  public StyleableBooleanProperty(StyleablePropertyMetaData StyleablePropertyMetaData, boolean initialValue) {
    super(initialValue);
    this.StyleablePropertyMetaData = StyleablePropertyMetaData;
  }
{code}

Issues:

...


In

...

the

...

future,

...

StyleableProperty

...

may

...

need

...

to

...

incorporate

...

support

...

for

...

attribute

...

selectors

...

and

...

animations.

StyleablePropertyMetaData

StyleablePropertyMetaData encapsulates the data needed to lookup a value and apply that value to a StyleableProperty. This includes the CSS property name, the default property value, and a link back to the corresponding StyleableProperty. The class is abstract and it is necessary to implement two methods which are invoked from the CSS engine:

Code Block

  
h3. StyleablePropertyMetaData
{{StyleablePropertyMetaData}} encapsulates the data needed to lookup a value and apply that value to a {{StyleableProperty}}. This includes the CSS property name, the default property value, and a link back to the corresponding {{StyleableProperty}}. The class is abstract and it is necessary to implement two methods which are invoked from the CSS engine:
{code}
/**
  * Check to see if the corresponding property on the given node is
  * settable. This method is called before any styles are looked up for the
  * given property. It is abstract so that the code can check if the property
  * is settable without expanding the property. Generally, the property is
  * settable if it is not null or is not bound.
  *
  * @param node The node on which the property value is being set
  * @return true if the property can be set.
  */
  public abstract boolean isSettable(N node);
  /**
  * Return the corresponding &lt;code&gt;javafx<code>javafx.beans.value.WriteableValue&lt;/code&gt;WriteableValue</code> for
  * the given Node. Note that calling this method will cause the property
  * to be expanded.
  * @param node
  * @return
  */
  public abstract WritableValue&lt;V&gt;WritableValue<V> getWritableValue(N node);{code}
 

The StyleableProperty is required to be a WritableValue. WritableValue is the interface which has the setValue method. If it isn't possible to setValue on the property, then it cannot be styled from CSS. The implementation is fairly consistent throughout the existing code, typically:

Code Block

   {{StyleableProperty}} is required to be a {{WritableValue}}. WritableValue is the interface which has the {{setValue}} method. If it isn't possible to {{setValue}} on the property, then it cannot be styled from CSS. The implementation is fairly consistent throughout the existing code, typically:
{code}
public abstract boolean isSettable(Watermark node) {
    return corner == null || corner.isBound() == false;
  }
  public abstract WritableValue&lt;String&gt;WritableValue<String> getWritableValue(Watermark node) {
    return cornerProperty();
  }
{code}

Note

...

that

...

isSettable

...

is

...

implemented

...

in

...

a

...

way

...

that

...

does

...

not

...

cause

...

the

...

expansion

...

of

...

the

...

property.

...

The isSettable check is performed before the CSS value is looked up. If the property is not settable, then there is no need to do the lookup. The getWritableValue method is invoked only if there is a CSS value to apply. Thus, if the property is not settable or there is no CSS value, the property is not expanded.
The CSS engine does not call setValue directly on the WritableValue. Rather, the code calls a set method on the StyleablePropertyMetaData which, in turn, calls applyStyle on the StyleableProperty. This level of indirection allows a StyleablePropertyMetaData to intercept the value before the calculated style value is applied (in other words, before setValue is called on the corresponding property). This is used primarily for Number based properties where the parameterized type is Number but the actual type might be Integer and so the value's intValue() method needs to be invoked.
The parameterization of StyleablePropertyMetaData is that it requires a Node since Node is the visible element of the scene graph. The V parameter is the type of the property's value. The corner property of Watermark would be declared as StyleablePropertyMetaData<Watermark, String>.

Code Block

public abstract class StyleablePropertyMetaData<N extends Node, V> {
  {{isSettable}} check is performed before the CSS value is looked up. If the property is not settable, then there is no need to do the lookup. The {{getWritableValue}} method is invoked only if there is a CSS value to apply. Thus, if the property is not settable or there is no CSS value, the property is not expanded.
The CSS engine does not call {{setValue}} directly on the {{WritableValue}}. Rather, the code calls a set method on the {{StyleablePropertyMetaData}} which, in turn, calls {{applyStyle}} on the {{StyleableProperty}}. This level of indirection allows a {{StyleablePropertyMetaData}} to intercept the value before the calculated style value is applied (in other words, before {{setValue}} is called on the corresponding property). This is used primarily for {{Number}} based properties where the parameterized type is {{Number}} but the actual type might be {{Integer}} and so the value's {{intValue()}} method needs to be invoked.
The parameterization of {{StyleablePropertyMetaData}} is that it requires a {{Node}} since {{Node}} is the visible element of the scene graph. The {{V}} parameter is the type of the property's value. The corner property of Watermark would be declared as {{StyleablePropertyMetaData<Watermark, String>.}}
{code}
public abstract class StyleablePropertyMetaData&lt;N extends Node, V&gt; {

  /**
  * Check to see if the corresponding property on the given node is
  * settable. This method is called before any styles are looked up for the
  * given property. It is abstract so that the code can check if the property
  * is settable without expanding the property. Generally, the property is
  * settable if it is not null or is not bound.
  *
  * @param node The node on which the property value is being set
  * @return true if the property can be set.
  */
  public abstract boolean isSettable(N node);
  /**
  * Return the corresponding &lt;code&gt;javafx corresponding <code>javafx.beans.value.WriteableValue&lt;/code&gt;WriteableValue</code> for
  * the given Node. Note that calling this method will cause the property
  * to be expanded.
  * @param node
  * @return
  */
  public abstract WritableValue&lt;V&gt;WritableValue<V> getWritableValue(N node);
 
  /**
  * Set the value of the corresponding property on the given Node.
  * @param node The node on which the property value is being set
  * @param value The value to which the property is set
  */
  public void set(N node, V value, Origin origin) {
    // details omitted, but this method ends up calling applyStyle in the StyleableProperty interface. 
  }

  private final String property;
  /**
  * @return the CSS property name
  */
  public final String getProperty() {
    return property;
  }

  private final V initialValue;
  /**
  * The initial value of a StyleablePropertyMetaData corresponds to the default
  * value of the WritableValue in code.
  * For example, the default value of Shape.fill is Color.BLACK and the
  * initialValue of Shape.CssMetaData.FILL is also Color.BLACK.
  * &lt;p&gt;<p>
  * There may be exceptions to this, however. The initialValue may depend
  * on the state of the Node. A ScrollBar has a default orientation of
  * horizontal. If the ScrollBar is vertical, however, this method should
  * return Orientation.VERTICAL. Otherwise, a vertical ScrollBar would be
  * incorrectly set to a horizontal ScrollBar when the initial value is
  * applied.
  * @return The initial value of the property, possibly null
  */
  public V getInitialValue(N node) {
    return initialValue;
  }
 
  private final List&lt;StyleablePropertyMetaData&gt;List<StyleablePropertyMetaData> subProperties;
  /**
  * The sub-properties refers to the constituent properties of this property,
  * if any. For example, "-fx-font-weight" is sub-property of "-fx-font".
  */
  public final List&lt;StyleablePropertyMetaData&gt;List<StyleablePropertyMetaData> getSubProperties() {
    return subProperties;
  }

  private final boolean inherits;
  /**
  * If true, the value of this property is the same as
  * the parent's computed value of this property.
  * @default false
  * @see &lt;a<a href="http://www.w3.org/TR/css3-cascade/#inheritance"&gt;CSS Inheritance&lt;/a&gt;>CSS Inheritance</a>
  */
  public final boolean isInherits() {
    return inherits;
  }

  /**
  * Construct a StyleablePropertyMetaData with the given parameters and no sub-properties.
  * @param property the CSS property
  * @param initalValue the default value of the corresponding property which may be null
  * @param inherits true if this property uses CSS inheritance
  * @param subProperties the sub-properties of this property. For example,
  * the -fx-font property has the sub-properties -fx-font-family,
  * -fx-font-size, -fx-font-weight, and -fx-font-style.
  */
  protected StyleablePropertyMetaData(
      final String property,
      final V initialValue,
      boolean inherits,
      final List&lt;StyleablePropertyMetaData&gt;List<StyleablePropertyMetaData> subProperties) {    
   
    this.property = property;
    this.initialValue = initialValue;
    this.inherits = inherits;
    this.subProperties = subProperties != null ? Collections.unmodifiableList(subProperties) : null;

 : null;    
   
    if (this.property == null)
      throw new IllegalArgumentException("property cannot be null");
  }

  /**
  * Construct a StyleablePropertyMetaData with the given parameters and no sub-properties.
  * @param property the CSS property
  * @param initalValue the default value of the corresponding property which may be null
  * @param inherits true if this property uses CSS inheritance
  */
  protected StyleablePropertyMetaData(
      final String property,
      final V initialValue,
      boolean inherits) {
    this(property, initialValue, inherits, null);
  }

  /**
  * Construct a StyleablePropertyMetaData with the given parameters, inherit set to
  * false and no sub-properties.
  * @param property the CSS property
  * @param initalValue the default value of the corresponding property which may be null
  */
  protected StyleablePropertyMetaData(
      final String property,
      final V initialValue) {
    this(property, initialValue, false, null);
  }

  /**
  * Construct a StyleablePropertyMetaData with the given parameters, initialValue is
  * null, inherit is set to false, and no sub-properties.
  * @param property the CSS property
  */
  protected StyleablePropertyMetaData(
      final String property) {
    this(property, false, null);
  }
}
{code}

Issues:

...


One

...

major

...

aspect

...

of

...

the

...

private

...

interface

...

has

...

been

...

omitted

...

from

...

the

...

public

...

interface

...

and

...

that

...

is

...

the

...

notion

...

of

...

a

...

Conveter.

...

A

...

Converter

...

takes

...

a

...

value

...

from

...

the

...

parser

...

(a

...

ParsedValue)

...

and

...

converts

...

it

...

to

...

the

...

corresponding

...

java

...

type.

...

So,

...

for

...

example,

...

a

...

ColorConverter

...

would

...

convert

...

a

...

ParsedValue

...

representing

...

a

...

Color

...

into

...

a

...

Color.

...

If

...

possible,

...

the

...

parser

...

performs

...

the

...

conversion.

...

But

...

this

...

is

...

not

...

always

...

possible

...

such

...

as

...

when

...

converting

...

an

...

em

...

size

...

to

...

an

...

absolute

...

pixel

...

value.

...

The

...

Converter

...

was

...

omitted

...

because

...

including

...

it

...

in

...

the

...

API

...

would

...

result

...

in

...

dragging

...

much

...

more

...

of

...

the

...

private

...

implementation

...

than

...

is

...

desired.

...

ParsedValue,

...

for

...

instance,

...

is

...

(in

...

my

...

opinion)

...

not

...

suitable

...

for

...

public

...

API

...

in

...

its

...

current

...

state

...

because

...

it

...

is

...

tightly

...

coupled

...

with

...

the

...

parser

...

and

...

the

...

Converters.

...

I'm

...

not

...

sure,

...

however,

...

that

...

this

...

public

...

API

...

can

...

exist

...

without

...

a

...

Converter

...

member,

...

but

...

I

...

believe

...

I

...

can

...

infer

...

the

...

Converter

...

from

...

the

...

WritableValue.

...

Alternatively,

...

some

...

type

...

indicator

...

could

...

be

...

used

...

or

...

a

...

set

...

of

...

CSS*Property

...

classes

...

(e.g.,

...

CSSBooleanProperty)

...

could

...

be

...

created.

Origin

Code Block


h3. Origin
{code}
/**
* Enumeration of the possible source or origin of a stylesheet and styles.
*/
public enum Origin {
  /** The stylesheet is a user-agent stylesheet */
  USER_AGENT,
  /** The value of a property was set by the user through a call to a set method */
  USER,
  /** The stylesheet is an external file */
  AUTHOR,
  /** The style is from the Node via setStyle */
  INLINE
}
{code  
}

h2. 

Additional

...

Node

...

API.

...

List<StyleablePropertyMetaData>

...

getStyleablePropertyMetaData()

...


This

...

method

...

returns

...

a

...

List

...

of

...

supported

...

StyleablePropertyMetaData.

...

The

...

list

...

should

...

include

...

not

...

only

...

the

...

properties

...

of

...

this

...

node,

...

but

...

the

...

properties

...

of

...

the

...

node's

...

super-class(es)

...

as

...

well.

...

This

...

method

...

is

...

called

...

frequently

...

and,

...

by

...

convention,

...

it

...

returns

...

a

...

static

...

list.

...

Since

...

this

...

static

...

list

...

may

...

be

...

used

...

by

...

other

...

node

...

classes,

...

the

...

convention

...

is

...

to

...

provide

...

a

...

public

...

static

...

method

...

that

...

returns

...

the

...

static

...

list:

...

public

...

static

...

List<StyleablePropertyMetaData>

...

getClassStyleablePropertyMetaData().

...

The

...

getStyleablePropertyMetaData()

...

method

...

is

...

used

...

by

...

the

...

CSS

...

engine

...

to

...

avoid

...

reflection.

...

A

...

typical

...

implementation

...

would

...

be:

{
Code Block
}
private static class StyleableProperties {
 
  private static final StyleablePropertyMetaData<Watermark, String> CORNER = new StyleablePropertyMetaData<Watermark, String>("-my-corner", "lower-right") {
    public abstract boolean isSettable(Watermark node) {
      return corner == null || corner.isBound() == false;
    }
    public abstract WritableValue<String> getWritableValue(Watermark node) {
      return cornerProperty();
    }
  }
  private static final List<StyleablePropertyMetaData> META_DATA;
  static {
    final List<StyleablePropertyMetaData> data = new ArrayList<StyleablePropertyMetaData>();
    Collections.addAll(data,
      Control.getClassStyleablePropertyMetaData(),
      CORNER
    );
    META_DATA = Collections.unmodifiableList(data);
  }
}

public static List&lt;StyleablePropertyMetaData&gt;List<StyleablePropertyMetaData> getClassStyleablePropertyMetaData() {
  return StyleableProperties.META_DATA;
}

public static List&lt;StyleablePropertyMetaData&gt;List<StyleablePropertyMetaData> getStyleablePropertyMetaDataMetaData() {
  return getClassStyleablePropertyMetaData();
}
{code}
h2. Pseudoclass
In the current implementation, the pattern for handling 

Pseudoclass

In the current implementation, the pattern for handling pseudo-class

...

state

...

is

...

to

...

override

...

the

...

invalidated

...

method

...

of

...

the

...

property

...

in

...

order

...

to

...

invoke

...

impl_pseudoClassStateChanged

...

which

...

sets

...

a

...

flag

...

indicating

...

that

...

the

...

node

...

needs

...

updating

...

by

...

CSS.

...

When

...

CSS

...

processes

...

the

...

update,

...

it

...

gets

...

the

...

node's

...

current

...

pseudo-class

...

state

...

by

...

calling

...

impl_getPseudoClassState()

...

.

...

Each

...

node

...

that

...

has

...

pseudo-class

...

state

...

overrides

...

impl_getPseudoClassState

...

.

...

A

...

typical

...

example

...

from

...

ButtonBase

...

,

...

where

...

ARMED_PSEUDOCLASS_STATE

...

is

...

a

...

bit

...

mask:

{
Code Block
}
  @Override public long impl_getPseudoClassState() {
    long mask = super.impl_getPseudoClassState();
    if (isArmed()) mask |= ARMED_PSEUDOCLASS_STATE;
    return mask;
  }
{code}

The

...

issue

...

with

...

promoting

...

this

...

implementation

...

to

...

public

...

API

...

is

...

that

...

it

...

is

...

susceptible

...

to

...

coding

...

errors.

...

One

...

might

...

forget

...

to

...

call

...

super.impl_getPsuedoClassState()

...

or

...

might

...

use

...

the

...

wrong

...

bit

...

mask,

...

either

...

accidentally

...

or

...

purposefully.

...

It

...

should

...

be

...

simple

...

enough

...

to

...

codify

...

this

...

pattern

...

in

...

a

...

*PseudoClass

...

class,

...

as

...

shown

...

in

...

the

...

an

...

example

...

below.

...

Note

...

that

...

this

...

PseudoClass

...

requires

...

a

...

bean

...

object

...

and

...

that

...

the

...

bean

...

must

...

be

...

a

...

Node

...

in

...

order

...

to

...

call

...

impl_pseudoClassStateChanged()

...

.

{
Code Block
}
public class BooleanPseudoClass extends SimpleBooleanProperty {

    public BooleanPseudoClass(String pseudoClass, Object bean, String name) {
        super(bean, name);
        this.pseudoClass = pseudoClass;
    }

    public BooleanPseudoClass(String pseudoClass, Object bean, String name, boolean initialValue) {
        super(bean, name, initialValue);
        this.pseudoClass = pseudoClass;
    }

    @Override protected void invalidated() {	
          ((Node)getBean()).pseudoClassStateChanged(pseudoClass);	
     }

     private final String pseudoClass;
}
{code}

But

...

this

...

approach

...

is

...

inadequate.

...

There

...

is

...

still

...

the

...

issue

...

that,

...

if

...

invalidate

...

is

...

overridden,

...

the

...

developer

...

might

...

neglect

...

to

...

call

...

super.invalidate().

...

Also

...

some

...

pseudo-class

...

states

...

are

...

implemented

...

in

...

read-only

...

properties

...

(Node

...

hover,

...

for

...

example),

...

some

...

are

...

boolean

...

and

...

others

...

are

...

not

...

boolean,

...

and

...

some

...

are

...

also

...

styleable

...

properties

...

(ScrollBar

...

orientation,

...

for

...

example).

...

ScrollBar's

...

orientation

...

pseudo-classes

...

are

...

"horizontal"

...

and

...

"vertical"

...

and

...

an

...

ObjectPseudoClass<Orientation>

...

would

...

still

...

require

...

that

...

invalidated

...

be

...

overridden.

...


Another

...

possible

...

alternative

...

is

...

to

...

make

...

use

...

of

...

attribute

...

selectors

...

for

...

3rd

...

party

...

controls

...

while

...

maintaining

...

private

...

implementation

...

of

...

pseudo-classes

...

for

...

the

...

SDK.

...

Attrtibute

...

selectors

...

may

...

not

...

be

...

feasible

...

under

...

current

...

time

...

constraints

...

and

...

performance

...

may

...

be

...

an

...

issue.

...


Therefore,

...

it

...

seems

...

that

...

the

...

best

...

alternative

...

is

...

to

...

promote

...

the

...

existing

...

private

...

implementation

...

to

...

public

...

API.

...

Although

...

this

...

involves

...

more

...

code

...

and

...

is

...

susceptible

...

to

...

error,

...

the

...

API

...

is

...

proven

...

and

...

promoting

...

it

...

involves

...

less

...

risk.

...

Pseudo-class

...

state

...

Currently,

...

pseudo-class

...

state

...

is

...

held

...

in

...

a

...

long.

...

Each

...

bit

...

in

...

the

...

long

...

value

...

corresponds

...

to

...

a

...

specific

...

pseudo-class.

...


private

...

static

...

final

...

long

...

HOVER_PSEUDOCLASS_STATE

...

=

...

PseudoClassStateBitMaskUtilities

...

.getMask("hover");

...


To

...

allow

...

for

...

a

...

larger

...

number

...

of

...

pseudo-classes,

...

a

...

long

...

[

...

]

...

will

...

be

...

returned

...

from

...

getPseudoClassState().

...

Each

...

individual

...

state,

...

such

...

as

...

HOVER_PSEUDOCLASS_STATE

...

will

...

still

...

be

...

a

...

long,

...

but

...

the

...

upper

...

4

...

bits

...

hold

...

the

...

index

...

of

...

the

...

mask

...

within

...

the

...

long

...

[

...

].

...

The

...

remaining

...

bits

...

are

...

the

...

bit

...

mask

...

itself.

...

This

...

allows

...

for

...

2^4

...

*

...

60,

...

or

...

1920,

...

possible

...

pseudo-classes.

...

This

...

technique

...

is

...

currently

...

used

...

for

...

handling

...

multiple

...

style

...

classes.

{
Code Block
}
/** 
  * A set of utilities for handling pseudo-class states as long[] bit mask.
  */
public final class PseudoClassStateBitMaskUtilities {
    //
    // The Long value is a bit mask. The upper 4 bits of the mask are used to
    // hold the index of the mask within the long[] and the remaining bits are
    // used to hold the mask value. If, for example, "foo" is the 96th entry in
    // styleClassMask, the upper 4 bits will be 0x01 (foo will be at mask[1]) 
    // and the remaining bits will have the 36th bit set. 
    //
    // When creating the long[] bit set, you get the value from maskBits,
    // mask and shift the upper 4 bits to get the index of the string in 
    // the long[], then or the value from maskBits with the mask[index]. 
    // In our example, "foo" will always be at mask[1]
    //
    private static final Map<String,Long> maskBits = new HashMap<String,Long>();

        
    // 4 is arbitrary but allows for 2^4 * 60, or 1920 unique strings.
    private static final int VALUE_BITS = Long.SIZE-4;
    
    // 0x0fffffffffffffff
    private static final long VALUE_MASK = ~(0xfL << VALUE_BITS);
        
    /** 
     * Get the bit mask for the give String. The upper 4 bits of the mask are used to
     * hold the index of the mask within the long[] and the remaining bits are
     * used to hold the mask value.
     * @param string The string to mask. 
     * @return The upper 4 bits is an index into the long[] mask representation 
     * of strings. The remaining bits are the bit mask for this string
     * within the mask[index]
     */
    public static long getMask(String string) {
        Long mask = maskBits.get(string);
        if (mask == null) {
            final int size = maskBits.size();
            final long element = size / VALUE_BITS; // use top bits for element
            final int exp = size % VALUE_BITS; // remaining bits for value
            mask = Long.valueOf(
                (element << VALUE_BITS) | (1L << exp) // same as Math.pow(2,exp)
            ); 
            maskBits.put(styleClass, mask);
        }
        return mask.longValue();
    }

    /*
     * Set a bit in an array of bit masks. 
     * @param maskArray The array of bit masks into which the bit from the mask param will be set.
     * @param mask The mask of the bit to set.
     * @return The resulting bit masks array. A new array is allocated if necessary. 
     */
     public long[] set(long[] maskArray, long mask) {
            final long element = (mask & ~VALUE_MASK);
            final int  index = (int)(element >>> VALUE_BITS);
            // need to grow?
            if (index >= maskArray.length) {
                final long[] temp = new long[index+1];
                System.arraycopy(maskArray, 0, temp, 0, maskArray.length);
                maskArray = temp;
            }
            maskArray[index] = maskArray[index] | mask;
            return maskArray;
     }

    /*
     * Clear a bit in an array of bit masks. 
     * @param maskArray The array of bit masks from which the bit from the mask param will be cleared.
     * @param mask The mask of the bit to clear.
     * @return The resulting bit masks array. 
     */
     public long[] clear(long[] maskArray, long mask) {
            final long element = (mask & ~VALUE_MASK);
            final int  index = (int)(element >>> VALUE_BITS);
            // mask in maskArray? if not, there is nothing to clear!
            if (index < maskArray.length) {
                long m = ~(mask & VALUE_MASK);
                maskArray[index] = maskArray[index] & m;
            }
            return maskArray;
     }

    public static List<String> getStyleClassStrings(long[] maskArray) {
        
        if (maskArray == null || maskArray.length == 0) return Collections.EMPTY_LIST;

        final Map<Long,String> stringMap = new HashMap<Long,String>();
        for (Map.Entry<String,Long> entry : maskBits.entrySet()) {
            stringMap.put(entry.getValue(), entry.getKey());
        }
        final List<String> strings = new ArrayList<String>();
        for(int index=0; index<maskArray.length; index++) {
            final long m = maskArray[index];
            final long element = (m & ~VALUE_MASK);
            for (int exp=0; exp < VALUE_BITS; exp++) {
                final long key = element | ((1L << exp) & m);
                if (key != 0) {
                    final String value = stringMap.get(key);
                    if (value != null) strings.add(value);
                }
            }
        }
        return strings;
    }    

}

Node API

Code Block

    /**
     {code}
h3. Node API
{code}

{code}
/*\*
* Used to notify that a pseudo-class has changed. Typically, this method is called from the invalidated 
     * method of a property that is used as a pseudo-class.
     * <code>
     * BooleanProperty myPseudoClassState = new BooleanPropertyBase(false) {
\*
*     *
     *           @Override public void invalidated() {
     *                pseudoClassStateChanged(MY_PSEUDO_CLASS_STATE);
     *           }
     * &nbsp;
*            
     *           @Override public Object getBean() {
     *               return MyControl.this;
     *           }
\     *
     *           @Override public String getName() {
     *               return "myPseudoClassState";
     *           }
     *       };
     * <code>
     * @param pseudoClass A bit mask that represents the pseudo-class that has changed.
\     */
    protected void pseudoClassStateChanged(long pseudoClass) {
       // if there are styles that use any of the given pseudoClasses, mark this node as needing css update
    }

    /*\*
**
     * The pseudo-class state of a Node is the state pertaining to this class and all of its super classes. This method is 
     * implemented by getting the pseudo-class state of the super class and then or'ing in the pseudo-class state of this class.
     * <code>
     * private static final long MY_PSEUDO_CLASS_STATE = PseudoClassStateBitMaskUtilities .getMask("my-pseudo-class-state");
\     *
     * @Override public long\[\] getPseudoClassState() {
     *      long\[\] state = super.getPseudoClassState(); 
     *      if (isMyPseudoClassState()) { PseudoClassStateBitMaskUtilities.set(state, MY_PSEUDO_CLASS_STATE); }
     *      return state;
     * }
     * <code>
     * @return long\[\] representing the state of this node and the state of its super classes. 
     * \
     */ 
    public BitMask getPseudoClassState() {
        long[] state = new long[1];
        if(isHover()) PseudoClassStateBitMaskUtilities.set(state, HOVER_PSEUDOCLASS_STATE);
        if(isPressed()) PseudoClassStateBitMaskUtilities.set(state, PRESSED_PSEUDOCLASS_STATE);
        if(isDisabled()) PseudoClassStateBitMaskUtilities.set(state, DISABLED_PSEUDOCLASS_STATE);
        if(isFocused()) PseudoClassStateBitMaskUtilities.set(state, FOCUSED_PSEUDOCLASS_STATE);
        if(impl_isShowMnemonics()) PseudoClassStateBitMaskUtilities.set(state, SHOW_MNEMONICS_PSEUDOCLASS_STATE);
        return mask;
    }
Overview
Content Tools
ThemeBuilder

Terms of Use
• License: GPLv2
• Privacy • Trademarks • Contact Us

Powered by a free Atlassian Confluence Open Source Project License granted to https://www.atlassian.com/software/views/opensource-community-additional-license-offer. Evaluate Confluence today.

  • Kolekti ThemeBuilder Powered by Atlassian Confluence 8.5.23
  • Kolekti ThemeBuilder printed.by.atlassian.confluence
  • Report a bug
  • Atlassian News
Atlassian
Kolekti ThemeBuilder EngineAtlassian Confluence
{"serverDuration": 1502, "requestCorrelationId": "25a3adf7a786e798"}