JEP 253: Prepare JavaFX UI Controls & CSS APIs for Modularization

OwnerJonathan Giles
Created2015/04/01 01:16
Updated2016/10/17 21:50
TypeFeature
StatusCompleted
Componentjavafx / controls
ScopeJDK
Discussionopenjfx dash dev at openjdk dot java dot net
EffortL
DurationL
Priority3
Reviewed byAnton Tarasov, Kevin Rushforth, Leif Samuelsson, Victor Dyakov
Endorsed byKevin Rushforth
Release9
Issue8076423

Summary

Define public APIs for the JavaFX UI controls and CSS functionality that is presently only available via internal APIs and will hence become inaccessible due to modularization.

Goals

Many developers who use the UI controls and CSS functionality of JavaFX have historically ignored the warnings to avoid the internal com.sun.* APIs. In many cases, to achieve a desired result, developers have no choice but to use these internal APIs. With the forthcoming release of Java 9, and in particular with the introduction of strong boundaries between modules in Project Jigsaw, developers will find that their code will no longer compile or run since the com.sun.* packages will no longer be accessible. The goal of this JEP is to define public APIs for the functionality presently offered by the internal APIs.

Non-Goals

Given the implications of modularization, that is, the forthcoming inaccessibility of the com.sun.* packages, there is no way to do this in a manner that retains any degree of backward compatibility. It is, therefore, not a goal of this JEP to retain backward compatibility. This does not mean that we can break anything we like; our intention is to only introduce new API (and evolve existing private API) that is directly broken by the enforcement of module boundaries. All other existing APIs that are not impacted by modularization will remain the same.

Success Metrics

Success can be measured in two ways:

Motivation

Without this work being done, many projects will need to significantly reduce the functionality that they offer, and for some projects this may prove fatal. For example, without access to the internal API that Scene Builder currently has, it may struggle to be viable---certainly its ability to offer functionality around CSS styling and manipulation of control properties will be severely undermined, and these are two of the core pieces of functionality of Scene Builder. The same argument holds for most other JavaFX-based projects with any degree of custom control or CSS implementation.

Description

This JEP is broken down into two semi-related subprojects, each of which is important in reaching the final goal. There is no particular order to which these projects must be undertaken.

Project One: Make UI control skins into public APIs

At present all skins are located in com.sun.javafx.scene.control.skin. This means that third parties who have extended a skin (e.g., the TextFieldSkin) to add additional functionality, to override an existing method, or otherwise to modify the visuals or behavior of the control skin, will be left without a functioning application in JDK 9. Of course, this is the fault of users who depended on non-public APIs, but we have long discussed making this API public so as to better enable third party modification of UI controls.

The intention is to move many JavaFX control skins into the appropriate public package, most probably javafx.scene.control.skin. There is no intent to also move the related behavior classes.

The vast bulk of this work is to review each existing skin class, ensuring the following:

This research is already quite far progressed in a separate sandbox repo, and whilst time-consuming, there are only a few classes of concern, such as utility classes, duplicated classes, and truly implementation-only classes, that need further analysis. On top of this, there is a handful of classes that may qualify for being brought into the javafx.scene.control package or, at the least, will need further consideration since they are not truly skins. These include FXVK (the virtual keyboard), ColorPalette, CustomColorDialog, DatePickerContent, and InputField. Finally, there are a few classes where, ideally, methods would be made private, except for the fact that other, implementation-only classes rely on this API. Solutions will be investigated for all of these issues, and no major concerns exist.

The intent of making the skins into public API in 9 is to ensure their continued availability. The API will be purposefully kept to the bare minimum and reduced as significantly as possible, with the intention to follow this up in subsequent releases with more useful APIs that developers request. As is well appreciated, APIs are (mostly) forever, so allowing for the public skin API to mature over a few update releases seems like the best course of action.

As of mid June, this project is at the point where almost all code is moved and cleaned up. The intention is to make this public in a JDK 9 build around mid-July to early August. The following is a list of all classes that have moved into javafx.scene.control.skin as public API:

These classes, as of mid-June, are stripped of almost any API that is not inherited from SkinBase. Moving forward, the intention is to add back useful API as feedback is received based on early access builds. Some classes, such as the text input controls and virtualised controls, have additional API already to support their functionality.

Project Two: Review and make public relevant CSS APIs

As with Project One, this project relates to bringing out into the public APIs that currently reside in com.sun.* packages. Again, this will require code review to minimise the API, as well as additional unit tests and vastly more documentation.

The driver for this work will be to continue to allow for Scene Builder to compile in JDK 9, with the appropriate modifications.

As of mid June, this project is at the point where almost all code is moved and cleaned up. The intention is to make this public in a JDK 9 build around mid-July to early August. The following is a list of all classes that have moved into javafx.css as public API:

CascadingStyle.java:public class CascadingStyle implements Comparable<CascadingStyle> {
CascadingStyle.java:    public Style getStyle() {
CascadingStyle.java:    public CascadingStyle(final Style style, Set<PseudoClass> pseudoClasses,
CascadingStyle.java:    public String getProperty() {
CascadingStyle.java:    public Selector getSelector() {
CascadingStyle.java:    public Rule getRule() {
CascadingStyle.java:    public StyleOrigin getOrigin() {
CascadingStyle.java:    public ParsedValueImpl getParsedValueImpl() {

CompoundSelector.java:final public class CompoundSelector extends Selector {
CompoundSelector.java:    public List<SimpleSelector> getSelectors() {
CompoundSelector.java:    public CompoundSelector(List<SimpleSelector> selectors, List<Combinator> relationships)
CompoundSelector.java:    public Match createMatch() {

CssError.java:public class CssError {
CssError.java:    public static void setCurrentScene(Scene scene) {
CssError.java:    public final String getMessage() {
CssError.java:    public CssError(String message) {
CssError.java:    public final static class PropertySetError extends CssError {
CssError.java:        public PropertySetError(CssMetaData styleableProperty,

Declaration.java:final public class Declaration {
Declaration.java:    public ParsedValue getParsedValue() {
Declaration.java:    public String getProperty() {
Declaration.java:    public Rule getRule() {

Rule.java:final public class Rule {
Rule.java:    public final ObservableList<Declaration> getDeclarations() {
Rule.java:    public final ObservableList<Selector> getSelectors() {
Rule.java:    public Stylesheet getStylesheet() {
Rule.java:    public StyleOrigin getOrigin() {

Selector.java:abstract public class Selector {
Selector.java:    public Rule getRule() {
Selector.java:    public void setOrdinal(int ordinal) {
Selector.java:    public int getOrdinal() {
Selector.java:    public abstract Match createMatch();
Selector.java:    public abstract boolean applies(Styleable styleable);
Selector.java:    public abstract boolean applies(Styleable styleable, Set<PseudoClass>[] triggerStates, int bit);
Selector.java:    public abstract boolean stateMatches(Styleable styleable, Set<PseudoClass> state);
Selector.java:    public static Selector createSelector(final String cssSelector) {
Selector.java:    protected void writeBinary(DataOutputStream os, StringStore stringStore)

SimpleSelector.java:final public class SimpleSelector extends Selector {
SimpleSelector.java:    public String getName() {
SimpleSelector.java:    public List<String> getStyleClasses() {
SimpleSelector.java:    public Set<StyleClass> getStyleClassSet() {
SimpleSelector.java:    public String getId() {
SimpleSelector.java:    public NodeOrientation getNodeOrientation() {

Size.java:final public class Size {
Size.java:    public Size(double value, SizeUnits units) {
Size.java:    public double getValue() {
Size.java:    public SizeUnits getUnits() {
Size.java:    public boolean isAbsolute() {
Size.java:    public double pixels(double multiplier, Font font) {
Size.java:    public double pixels(Font font) {
Size.java:    public double pixels() {

Style.java:final public class Style {
Style.java:    public Selector getSelector() {
Style.java:    public Declaration getDeclaration() {
Style.java:    public Style(Selector selector, Declaration declaration) {

Stylesheet.java:public class Stylesheet {
Stylesheet.java:    public String getUrl() {
Stylesheet.java:    public StyleOrigin getOrigin() {
Stylesheet.java:    public void setOrigin(StyleOrigin origin) {
Stylesheet.java:    public List<Rule> getRules() {
Stylesheet.java:    public List<FontFace> getFontFaces() {
Stylesheet.java:    public static Stylesheet loadBinary(URL url) throws IOException {
Stylesheet.java:    public static void convertToBinary(File source, File destination) throws IOException {

CssParser.java:final public class CssParser {
CssParser.java:    public CssParser() {
CssParser.java:    public Stylesheet parse(final String stylesheetText) {
CssParser.java:    public Stylesheet parse(final URL url) throws IOException {
CssParser.java:    public Stylesheet parseInlineStyle(final Styleable node) {
CssParser.java:    public ParsedValueImpl parseExpr(String property, String expr) {
CssParser.java:    public static ObservableList<CssError> errorsProperty() {

Summary

The end result of these two projects is the creation of:

Testing

Testing will be limited to additional unit tests that have no special platform or hardware requirements.

Risks and Assumptions

The primary risk is that the scope of work exceeds what is anticipated. Some research has been done to better understand the requirements, but there is no denying that producing good APIs will require a decent investment of time.