跳到主要内容

JEP 253:为模块化准备 JavaFX UI 控件和 CSS API

概括

为 JavaFX UI 控件和 CSS 功能定义公共 API,这些功能目前只能通过内部 API 获得,因此由于模块化而将变得无法访问。

目标

许多使用 JavaFX 的 UI 控件和 CSS 功能的开发人员历来都忽略警告以避免使用内部com.sun.*API。在许多情况下,为了达到预期的结果,开发人员别无选择,只能使用这些内部 API。随着 Java 9 即将发布,特别是在Jigsaw 项目中引入了模块之间的强边界,开发人员会发现他们的代码将不再编译或运行,因为com.sun.*包将不再可访问。该 JEP 的目标是为内部 API 目前提供的功能定义公共 API。

非目标

考虑到模块化的影响,即即将出现的com.sun.*软件包不可访问性,没有办法以保留任何程度的向后兼容性的方式来做到这一点。因此,保留向后兼容性并不是本 JEP 的目标。这并不意味着我们可以破坏任何我们喜欢的东西;我们的目的是只引入新的 API(并发展现有的私有 API),这些 API 会被模块边界的强制执行直接打破。所有其他不受模块化影响的现有 API 将保持不变。

成功指标

成功可以通过两种方式来衡量:

  • 依赖于 JavaFX 内部 API 的项目(特别是 Scene Builder、ControlsFX 和 JFXtras)在更新到新 API 后可以继续工作,而不会丢失功能。这三个项目都是开源的,它们将提供一个优秀的测试平台,以确保提供所有必要的 API。

  • 围绕新 API 的讨论结果(例如控制行为所需的输入映射工作)带来了开发人员长期以来所要求的改进功能。最终,如果一切按计划进行,第三方控件应该可以构建,而不依赖于内部 API。

动机

如果不完成这项工作,许多项目将需要显着减少它们提供的功能,对于某些项目来说,这可能是致命的。例如,如果无法访问 Scene Builder 目前拥有的内部 API,它可能很难生存——当然,它提供围绕 CSS 样式和控制属性操作的功能的能力将被严重削弱,而这是两个核心场景生成器的部分功能。同样的论点也适用于大多数其他具有任何程度的自定义控制或 CSS 实现的基于 JavaFX 的项目。

描述

该 JEP 分为两个半相关的子项目,每个子项目对于实现最终目标都很重要。这些项目的实施没有特定的顺序。

项目一:将UI控件皮肤做成公共API

目前所有皮肤都位于com.sun.javafx.scene.control.skin.这意味着扩展皮肤(例如TextFieldSkin)以添加附加功能、覆盖现有方法或以其他方式修改控件皮肤的视觉效果或行为的第三方将在 JDK 9 中失去正常运行的应用程序。当然,这是依赖非公开 API 的用户的错,但我们很早就讨论过将这个 API 公开,以便更好地允许第三方修改 UI 控件。

目的是将许多 JavaFX 控件皮肤移动到适当的公共包中,很可能是javafx.scene.control.skin.无意也移动相关的行为类。

这项工作的大部分内容是审查每个现有的皮肤类别,确保满足以下条件:

  • 皮肤之间存在 API 一致性,

  • 存在高质量的 Javadoc 和单元测试,并且

  • 通过将公共方法更改为私有方法,将每个类中的 API 保持在最低限度。

这项研究已经在一个单独的沙箱存储库中取得了相当大的进展,虽然很耗时,但只有少数几个类需要进一步分析,例如实用程序类、重复的类和真正仅实现的类。除此之外,还有一些类别可能有资格被纳入该javafx.scene.control包中,或者至少需要进一步考虑,因为它们不是真正的皮肤。其中包括FXVK(虚拟键盘)、ColorPaletteCustomColorDialogDatePickerContentInputField。最后,有一些类,理想情况下,方法应设为私有,但其他仅实现的类依赖于此 API。将研究所有这些问题的解决方案,并且不存在重大问题。

在 9 中将皮肤设为公共 API 的目的是确保它们的持续可用性。 API 将有目的地保持在最低限度并尽可能显着减少,目的是在后续版本中跟进开发人员要求的更有用的 API。众所周知,API(大部分)是永远存在的,因此允许公共皮肤 API 经过几次更新版本的成熟似乎是最好的做法。

截至六月中旬,该项目几乎所有代码都已被移动和清理。目的是在 7 月中旬到 8 月初左右的 JDK 9 版本中公开这一点。以下是已移至javafx.scene.control.skin公共 API 的所有类的列表:

  • AccordionSkin
  • ButtonBarSkin
  • ButtonSkin
  • CellSkinBase
  • CheckBoxSkin
  • ChoiceBoxSkin
  • ColorPickerSkin
  • ComboBoxBaseSkin
  • ComboBoxListViewSkin
  • ComboBoxPopupControl
  • ContextMenuSkin
  • DateCellSkin
  • DatePickerSkin
  • HyperlinkSkin
  • LabelSkin
  • LabeledSkinBase
  • ListCellSkin
  • ListViewSkin
  • MenuBarSkin
  • MenuButtonSkin
  • MenuButtonSkinBase
  • NestedTableColumnHeader
  • PaginationSkin
  • ProgressBarSkin
  • ProgressIndicatorSkin
  • RadioButtonSkin
  • ScrollBarSkin
  • ScrollPaneSkin
  • SeparatorSkin
  • SliderSkin
  • SpinnerSkin
  • SplitMenuButtonSkin
  • SplitPaneSkin
  • TabPaneSkin
  • TableCellSkin
  • TableCellSkinBase
  • TableColumnHeader
  • TableHeaderRow
  • TableRowSkin
  • TableRowSkinBase
  • TableViewSkin
  • TableViewSkinBase
  • TextAreaSkin
  • TextFieldSkin
  • TextInputControlSkin
  • TitledPaneSkin
  • ToggleButtonSkin
  • ToolBarSkin
  • TooltipSkin
  • TreeCellSkin
  • TreeTableCellSkin
  • TreeTableRowSkin
  • TreeTableViewSkin
  • TreeViewSkin
  • VirtualContainerBase
  • VirtualFlow

截至 6 月中旬,这些类几乎删除了所有不是从 SkinBase 继承的 API。展望未来,我们的目的是根据早期访问版本收到反馈后添加有用的 API。某些类(例如文本输入控件和虚拟化控件)已经具有额外的 API 来支持其功能。

项目二:审核并公开相关CSS API

与项目一一样,该项目涉及将当前驻留在包中的公共 API 引入com.sun.*。同样,这将需要代码审查以最小化 API,以及额外的单元测试和更多的文档。

这项工作的驱动力将是继续允许场景生成器在 JDK 9 中进行编译,并进行适当的修改。

截至六月中旬,该项目几乎所有代码都已被移动和清理。目的是在 7 月中旬到 8 月初左右的 JDK 9 版本中公开这一点。以下是已移至javafx.css公共 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() {

概括

这两个项目的最终结果是创建:

  • javafx.scene.control.skin经过审查、记录和测试后,用于 UI 控件皮肤的新包;

  • 必要的 CSS 相关类的移动、审查、文档记录和测试。

测试

测试将仅限于没有特殊平台或硬件要求的附加单元测试。

风险和假设

主要风险是工作范围超出预期。为了更好地理解需求,已经进行了一些研究,但不可否认的是,生产好的 API 需要大量的时间投入。