• 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 12

changes.mady.by.user J. Duke

Saved on Dec 12, 2012

compared with

New Version 13

changes.mady.by.user J. Duke

Saved on Dec 12, 2012

  • Previous Change: Difference between versions 11 and 12
  • Next Change: Difference between versions 13 and 14
  • View Page History

Key

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

...

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();
     }

At this point, the copyrightAngle can be styled through CSS. For example, to display the copyright from lower left to upper right:

Code Block
    .watermark { -my-copyright-angle: -45; }

...

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";
                }
            };
    }

CSS calls getPseudoClassStates() which returns the pseudo-class state of the node. The node's pseudo-class state should include the state from the node class and all its super classes. Therefore, the first thing getPseudoClassStates() should do is call super.getPseudoClassStates().

Code Block
    public PseudoClass.States getPseudoClassStates() {
        PseudoClassStates pseudoClassStates = super.getPseudoClassStates();
        if (isViewed()) pseudoClassStates = pseudoClassStates.addState(VIEWED_PSEUDO_CLASS);
        return pseudoClassStates;
    }

With this framework in place, the following style can be used to make the watermark more transparent if it has been viewed.

Code Block
    .watermark:viewed { -fx-opacity: 30%; }

...

The getCssMetaData method is useful for getting from a StyleableProperty to the corresponding CssMetaData, which is useful for tooling and unit testing. For example, one can get the CssMetaData of the fillProperty of a Rectangle:

Code Block
    Rectangle rect = new Rectangle(50,50);
    CssMetaData fillCssMetaData = ((StyleableProperty)rect.fillProperty()).getCssMetaData();
    System.out.println("Use " + fillCssMetaData.getProperty() + " to style Rectangle fill");

...

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 the classes have a constructor taking a CssMetaData arg and another taking a CssMetaData arg and an initial value. For example, StyleableBooleanProperty has the following constructors:

...

public class SImpleStyleableBooleanProperty extends SImpleBooleanProperty implements StyleableProperty<Boolean>
public class SImpleStyleableFloatProperty extends SImpleFloatProperty implements StyleableProperty<Float>
public class SImpleStyleableDoubleProperty extends SImpleDoubleProperty implements StyleableProperty<Double>
public class SImpleStyleableIntegerProperty extends SImpleIntegerProperty implements StyleableProperty<Integer>
public class SImpleStyleableLongProperty extends SImpleLongProperty implements StyleableProperty<Long>
public class SImpleStyleableStringProperty extends SImpleStringProperty implements StyleableProperty<String>
public class SImpleStyleableObjectProperty<T> extends SImpleObjectProperty<T> implements StyleableProperty<T>

Each of these simple classes have a constructor taking an Object which is the property bean and a String which is the property name in combination with a CssMetaData arg and another taking a CssMetaData arg and an initial value. For example, SimpleStyleableBooleanProperty has the following constructors:

Code Block
/**
  * The constructor of the {@code SimpleStyleableBooleanProperty}.
  */
  public SimpleStyleableBooleanProperty(Object bean, String name, CssMetaData CssMetaData) {
    super(bean, name);
    this.CssMetaData = CssMetaData;
  }

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

/**
  * The constructor of the {@code SimpleStyleableBooleanProperty}.
  */
  public SimpleStyleableBooleanProperty(CssMetaData CssMetaData) {
    super();
    this.CssMetaData = CssMetaData;
  }

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

Issues:
In the future, StyleableProperty may need to incorporate support for attribute selectors and animations.

...

PseudoClass contains two inner classes, State and States TBD: bad naming?. PseudoClass.State is an immutable object that represents one pseudo-class state. PseudoClass.States is a mutable object that is returned from getPseudoClassStates() and represents the summation of individual PseudoClass.State values that are in effect.

...

Typical usage is:

Code Block
    private static final PseudoClassState MAGIC_PSEUDO_CLASS_STATE = PseudoClassState.getState("xyzzy");

    @Override public PseudoClass.States getPseudoClassStates() {
          PseudoClass.States states = super.getPseudoClassStates();
          if (isMagic()) states.addState(MAGIC_PSEUDO_CLASS_STATE);
          return states;
     }

...

Code Block
package javafx.scene;

public final class PseudoClass {

    /**
     * There is only one PseudoClass.State instance for a given pseudoClass. 
     * @return The PseudoClass.State for the given pseudoClass. Will not return null.
     */
    public static PseudoClass.State getState(String pseudoClass) {
        // Use pseudoClass as key to find State in a Map<String,State> 
        // If not found, create it and add to the map.
        // return the PseudoClass.State
    }

    /**
     * @return A new PseudoClass.States instance
     */
    public static PseudoClass.States createStatesInstance() {
    }

    public final class State {
    
        /** Cannot create an instance of State except through PseudoClass static methods */
        private State() {
        }

       @Override public String toString() {
            // return the String that was used to create this PseudoClassState pseudo-class, e.g. "hover"
        }	

    }

    public final class States {
    
        /** 
         * Add the state to the current set of states. The result of the operation will be the union of
         * the existing set of states and the added state. 
         * @param state The state to add
         */
        public void addState(PseudoClass.State state) {
        }

        /** 
         * Remove the state to the current states. The result of the operation will be the relative complement of
         * the removed state in the existing set of states. This implies that removing a state that is not in the
         * existing set of states had no effect.   
         * @param state The state to remove
         */
        public void removeState(PseudoClass.State state) {
        }

        /**
         * Reset the set of states to the empty set.
         */
        public void clear() {
        }

        /** @return The list of PseudoClass.State that are represented by this States object */
        public List<PseudoClass.State> getStates() {
        }	

        /** Cannot create an instance of States except through PseudoClass static methods */
        private States() {
        }

    }

}

...

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": 332, "requestCorrelationId": "b05baa4941a12e1a"}