import javafx.geometry.Orientation; import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.BorderPane; import javafx.scene.layout.FlowPane; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; import javafx.scene.layout.StackPane; import javafx.scene.layout.TilePane; import javafx.scene.layout.VBox; import com.sun.javafx.geom.BaseBounds; import com.sun.javafx.geom.transform.BaseTransform; /** * This class implements a workaround for RT-23351 which is necessary for * JavaFX runtimes prior to and including JavaFX 2.2.1. *
** Bounds calculation of a parent node which at any time during its existence * had more than 10 child nodes might produce incorrect values. This happens * when the parent node or some of its ancestor nodes in the scene graph * hierarchy are transformed with a transformation which differs from a simple * identity or translation. Incorrectly calculated bounds have negative impact * on other JavaFX functionality including but not limited to dirty area * management (causing disappearing or non-updated nodes) and mouse picking * (mouse events not delivered correctly, hover functionality broken, etc.). *
** This class provides factory methods for various JavaFX parent nodes. If * executed on JavaFX runtimes prior to and including JavaFX 2.2.1, the * factory methods return patched parent nodes, which don't suffer from the * described problem. For runtimes greater than JavaFX 2.2.1, they create * un-patched parent nodes, because these runtimes already include the fix for * RT-23351. *
** Setting of the {@code rt23351.enable.workaround} system property to * {@code true} or {@code false} overrides the JavaFX runtime version detection * mechanism of this class and directly controls whether its factory methods * return patched or un-patched parent nodes. *
*/ public final class RT23351_Workaround { private static final ContainerFactory CONTAINER_FACTORY = enableWorkaround() ? new PatchedContainerFactory() : new NormalContainerFactory(); private RT23351_Workaround() { } public static Group createGroup(final Node... children) { return CONTAINER_FACTORY.createGroup(children); } public static Pane createPane() { return CONTAINER_FACTORY.createPane(); } public static HBox createHBox() { return CONTAINER_FACTORY.createHBox(0); } public static HBox createHBox(final double spacing) { return CONTAINER_FACTORY.createHBox(spacing); } public static VBox createVBox() { return CONTAINER_FACTORY.createVBox(0); } public static VBox createVBox(final double spacing) { return CONTAINER_FACTORY.createVBox(spacing); } public static AnchorPane createAnchorPane() { return CONTAINER_FACTORY.createAnchorPane(); } public static BorderPane createBorderPane() { return CONTAINER_FACTORY.createBorderPane(); } public static FlowPane createFlowPane() { return CONTAINER_FACTORY.createFlowPane(Orientation.HORIZONTAL, 0, 0); } public static FlowPane createFlowPane(final Orientation orientation) { return CONTAINER_FACTORY.createFlowPane(orientation, 0, 0); } public static FlowPane createFlowPane(final double hgap, final double vgap) { return CONTAINER_FACTORY.createFlowPane(Orientation.HORIZONTAL, hgap, vgap); } public static FlowPane createFlowPane(final Orientation orientation, final double hgap, final double vgap) { return CONTAINER_FACTORY.createFlowPane(orientation, hgap, vgap); } public static GridPane createGridPane() { return CONTAINER_FACTORY.createGridPane(); } public static StackPane createStackPane() { return CONTAINER_FACTORY.createStackPane(); } public static TilePane createTilePane() { return CONTAINER_FACTORY.createTilePane(Orientation.HORIZONTAL, 0, 0); } public static TilePane createTilePane(final Orientation orientation) { return CONTAINER_FACTORY.createTilePane(orientation, 0, 0); } public static TilePane createTilePane(final double hgap, final double vgap) { return CONTAINER_FACTORY.createTilePane(Orientation.HORIZONTAL, hgap, vgap); } public static TilePane createTilePane(final Orientation orientation, final double hgap, final double vgap) { return CONTAINER_FACTORY.createTilePane(orientation, hgap, vgap); } private static boolean enableWorkaround() { final String enableWorkaround = System.getProperty("rt23351.enable.workaround"); if (enableWorkaround != null) { return enableWorkaround.equalsIgnoreCase("true"); } final String versionString = System.getProperty("javafx.runtime.version"); return (versionString != null) && (versionString.startsWith("2.0.") || versionString.startsWith("2.1.") || versionString.startsWith("2.2.0") || versionString.startsWith("2.2.1")); } private interface ContainerFactory { Group createGroup(Node... children); Pane createPane(); HBox createHBox(double spacing); VBox createVBox(double spacing); AnchorPane createAnchorPane(); BorderPane createBorderPane(); FlowPane createFlowPane(Orientation orientation, double hgap, double vgap); GridPane createGridPane(); StackPane createStackPane(); TilePane createTilePane(Orientation orientation, double hgap, double vgap); } private static final class NormalContainerFactory implements ContainerFactory { public Group createGroup(final Node... children) { return new Group(children); } public Pane createPane() { return new Pane(); } public HBox createHBox(final double spacing) { return new HBox(spacing); } public VBox createVBox(final double spacing) { return new VBox(spacing); } public AnchorPane createAnchorPane() { return new AnchorPane(); } public BorderPane createBorderPane() { return new BorderPane(); } public FlowPane createFlowPane(final Orientation orientation, final double hgap, final double vgap) { return new FlowPane(orientation, hgap, vgap); } public GridPane createGridPane() { return new GridPane(); } public StackPane createStackPane() { return new StackPane(); } public TilePane createTilePane(final Orientation orientation, final double hgap, final double vgap) { return new TilePane(orientation, hgap, vgap); } } private static final class PatchedContainerFactory implements ContainerFactory { public Group createGroup(final Node... children) { return new Group(children) { @Override public BaseBounds impl_computeGeomBounds( final BaseBounds bounds, final BaseTransform tx) { if (!tx.isTranslateOrIdentity()) { super.impl_computeGeomBounds( bounds, BaseTransform.IDENTITY_TRANSFORM); } return super.impl_computeGeomBounds(bounds, tx); } }; } public Pane createPane() { return new Pane() { @Override public BaseBounds impl_computeGeomBounds( final BaseBounds bounds, final BaseTransform tx) { if (!tx.isTranslateOrIdentity()) { super.impl_computeGeomBounds( bounds, BaseTransform.IDENTITY_TRANSFORM); } return super.impl_computeGeomBounds(bounds, tx); } }; } public HBox createHBox(final double spacing) { return new HBox(spacing) { @Override public BaseBounds impl_computeGeomBounds( final BaseBounds bounds, final BaseTransform tx) { if (!tx.isTranslateOrIdentity()) { super.impl_computeGeomBounds( bounds, BaseTransform.IDENTITY_TRANSFORM); } return super.impl_computeGeomBounds(bounds, tx); } }; } public VBox createVBox(final double spacing) { return new VBox(spacing) { @Override public BaseBounds impl_computeGeomBounds( final BaseBounds bounds, final BaseTransform tx) { if (!tx.isTranslateOrIdentity()) { super.impl_computeGeomBounds( bounds, BaseTransform.IDENTITY_TRANSFORM); } return super.impl_computeGeomBounds(bounds, tx); } }; } public AnchorPane createAnchorPane() { return new AnchorPane() { @Override public BaseBounds impl_computeGeomBounds( final BaseBounds bounds, final BaseTransform tx) { if (!tx.isTranslateOrIdentity()) { super.impl_computeGeomBounds( bounds, BaseTransform.IDENTITY_TRANSFORM); } return super.impl_computeGeomBounds(bounds, tx); } }; } public BorderPane createBorderPane() { return new BorderPane() { @Override public BaseBounds impl_computeGeomBounds( final BaseBounds bounds, final BaseTransform tx) { if (!tx.isTranslateOrIdentity()) { super.impl_computeGeomBounds( bounds, BaseTransform.IDENTITY_TRANSFORM); } return super.impl_computeGeomBounds(bounds, tx); } }; } public FlowPane createFlowPane(final Orientation orientation, final double hgap, final double vgap) { return new FlowPane(orientation, hgap, vgap) { @Override public BaseBounds impl_computeGeomBounds( final BaseBounds bounds, final BaseTransform tx) { if (!tx.isTranslateOrIdentity()) { super.impl_computeGeomBounds( bounds, BaseTransform.IDENTITY_TRANSFORM); } return super.impl_computeGeomBounds(bounds, tx); } }; } public GridPane createGridPane() { return new GridPane() { @Override public BaseBounds impl_computeGeomBounds( final BaseBounds bounds, final BaseTransform tx) { if (!tx.isTranslateOrIdentity()) { super.impl_computeGeomBounds( bounds, BaseTransform.IDENTITY_TRANSFORM); } return super.impl_computeGeomBounds(bounds, tx); } }; } public StackPane createStackPane() { return new StackPane() { @Override public BaseBounds impl_computeGeomBounds( final BaseBounds bounds, final BaseTransform tx) { if (!tx.isTranslateOrIdentity()) { super.impl_computeGeomBounds( bounds, BaseTransform.IDENTITY_TRANSFORM); } return super.impl_computeGeomBounds(bounds, tx); } }; } public TilePane createTilePane(final Orientation orientation, final double hgap, final double vgap) { return new TilePane(orientation, hgap, vgap) { @Override public BaseBounds impl_computeGeomBounds( final BaseBounds bounds, final BaseTransform tx) { if (!tx.isTranslateOrIdentity()) { super.impl_computeGeomBounds( bounds, BaseTransform.IDENTITY_TRANSFORM); } return super.impl_computeGeomBounds(bounds, tx); } }; } } }