Acercamiento a JavaFx: ScrollEvent se consume cuando el tamaño del contenido excede la ventana de ScrollPane

Tengo una aplicación que requiere zoom dentro de un ScrollPane, pero con mi enfoque actual todavía estoy enfrentando 2 desafíos. Para replicar el problema, he escrito una pequeña aplicación ZoomApp en la que encontrará el código que se encuentra debajo. Su funcionalidad limitada permite acercar y alejar (usando Ctrl + Rueda del Ratón) en algunas formas arbitrarias. Cuando el contenido ampliado crece fuera de los límites de la ventana, las barras de desplazamiento deberían aparecer.

Desafío 1. Cuando aparece la barra de desplazamiento como consecuencia degrupo interno aumenta su tamaño, el ScrollEvent ya no llega a mi ZoomHandler. En su lugar, comenzamos a desplazarnos hacia abajo en la ventana, hasta que llega a la parte inferior, cuando el zoom nuevamente funciona como se espera. Pensé que tal vez

scrollPane.setPannable(false);

Haría una diferencia, pero no. ¿Cómo debe evitarse este comportamiento no deseado?

Desafío 2. ¿Cómo voy a centrarme?grupo interno dentro del scrollPane, sin recurrir a pintar un píxel en la esquina superior izquierda degrupo interno ¿Con el delta deseado a las plazas?

Como una nota al margen, de acuerdo con el JavaDoc para ScrollPane:"Si una aplicación desea que el desplazamiento se base en los límites visuales del nodo (para contenido escalado, etc.), es necesario envolver el nodo de desplazamiento en un Grupo". Esta es la razón por la que tengo ungrupo interno y ungrupo externo dentro de ScrollPane.

Cualquier sugerencia que me guíe a la solución es muy apreciada por este novato de JavaFX.

import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.SceneBuilder;
import javafx.scene.control.ScrollPane;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

/**
 * Demo of a challenge I have with zooming inside a {@code ScrollPane}.
 * <br>
 * I am running JavaFx 2.2 on a Mac. {@code java -version} yields:
 * <pre>
 * java version "1.7.0_09"
 * Java(TM) SE Runtime Environment (build 1.7.0_09-b05)
 * Java HotSpot(TM) 64-Bit Server VM (build 23.5-b02, mixed mode)
 * </pre>
 * 6 rectangles are drawn, and can be zoomed in and out using either
 * <pre>
 * Ctrl + Mouse Wheel
 * or Ctrl + 2 fingers on the pad.
 * </pre>
 * It reproduces a problem I experience inside an application I am writing.
 * If you magnify to {@link #MAX_SCALE}, an interesting problem occurs when you try to zoom back to {@link #MIN_SCALE}. In the beginning
 * you will see that the {@code scrollPane} scrolls and consumes the {@code ScrollEvent} until we have scrolled to the bottom of the window.
 * Once the bottom of the window is reached, it behaves as expected (or at least as I was expecting).
 *
 * @author Skjalg Bjørndal
 * @since 2012.11.05
 */
public class ZoomApp extends Application {

    private static final int WINDOW_WIDTH = 800;
    private static final int WINDOW_HEIGHT = 600;

    private static final double MAX_SCALE = 2.5d;
    private static final double MIN_SCALE = .5d;

    private class ZoomHandler implements EventHandler<ScrollEvent> {

        private Node nodeToZoom;

        private ZoomHandler(Node nodeToZoom) {
            this.nodeToZoom = nodeToZoom;
        }

        @Override
        public void handle(ScrollEvent scrollEvent) {
            if (scrollEvent.isControlDown()) {
                final double scale = calculateScale(scrollEvent);
                nodeToZoom.setScaleX(scale);
                nodeToZoom.setScaleY(scale);
                scrollEvent.consume();
            }
        }

        private double calculateScale(ScrollEvent scrollEvent) {
            double scale = nodeToZoom.getScaleX() + scrollEvent.getDeltaY() / 100;

            if (scale <= MIN_SCALE) {
                scale = MIN_SCALE;
            } else if (scale >= MAX_SCALE) {
                scale = MAX_SCALE;
            }
            return scale;
        }
    }

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) throws Exception {

        final Group innerGroup = createSixRectangles();
        final Group outerGroup = new Group(innerGroup);

        final ScrollPane scrollPane = new ScrollPane();
        scrollPane.setContent(outerGroup);
        scrollPane.setOnScroll(new ZoomHandler(innerGroup));

        StackPane stackPane = new StackPane();
        stackPane.getChildren().add(scrollPane);

        Scene scene = SceneBuilder.create()
                .width(WINDOW_WIDTH)
                .height(WINDOW_HEIGHT)
                .root(stackPane)
                .build();

        stage.setScene(scene);
        stage.show();
    }

    private Group createSixRectangles() {
        return new Group(
                createRectangle(0, 0), createRectangle(110, 0), createRectangle(220, 0),
                createRectangle(0, 110), createRectangle(110, 110), createRectangle(220, 110),
                createRectangle(0, 220), createRectangle(110, 220), createRectangle(220, 220)
        );
    }

    private Rectangle createRectangle(int x, int y) {
        Rectangle rectangle = new Rectangle(x, y, 100, 100);
        rectangle.setStroke(Color.ORANGERED);
        rectangle.setFill(Color.ORANGE);
        rectangle.setStrokeWidth(3d);
        return rectangle;
    }
}

Respuestas a la pregunta(3)

Su respuesta a la pregunta