modules/controls/src/main/java/javafx/scene/control/skin/ScrollPaneSkin.java

Print this page
rev 9240 : 8076423: JEP 253: Prepare JavaFX UI Controls & CSS APIs for Modularization

*** 21,32 **** * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ ! package com.sun.javafx.scene.control.skin; import com.sun.javafx.scene.traversal.ParentTraversalEngine; import javafx.animation.Animation.Status; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; --- 21,34 ---- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ ! package javafx.scene.control.skin; + import com.sun.javafx.scene.control.Properties; + import com.sun.javafx.scene.control.behavior.BehaviorBase; import com.sun.javafx.scene.traversal.ParentTraversalEngine; import javafx.animation.Animation.Status; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline;
*** 42,70 **** import javafx.geometry.Bounds; import javafx.geometry.Orientation; import javafx.scene.AccessibleAttribute; import javafx.scene.Cursor; import javafx.scene.Node; import javafx.scene.control.ScrollBar; import javafx.scene.control.ScrollPane; import javafx.scene.control.ScrollPane.ScrollBarPolicy; import javafx.scene.input.MouseEvent; import javafx.scene.input.ScrollEvent; import javafx.scene.input.TouchEvent; import javafx.scene.layout.StackPane; import javafx.scene.shape.Rectangle; import javafx.util.Duration; import com.sun.javafx.util.Utils; import com.sun.javafx.scene.control.behavior.ScrollPaneBehavior; - import com.sun.javafx.scene.traversal.TraverseListener; import static com.sun.javafx.scene.control.skin.Utils.*; import javafx.geometry.Insets; ! public class ScrollPaneSkin extends BehaviorSkinBase<ScrollPane, ScrollPaneBehavior> implements TraverseListener { /*************************************************************************** * * ! * UI Subcomponents * * * **************************************************************************/ private static final double DEFAULT_PREF_SIZE = 100.0; --- 44,82 ---- import javafx.geometry.Bounds; import javafx.geometry.Orientation; import javafx.scene.AccessibleAttribute; import javafx.scene.Cursor; import javafx.scene.Node; + import javafx.scene.control.Button; + import javafx.scene.control.Control; import javafx.scene.control.ScrollBar; import javafx.scene.control.ScrollPane; import javafx.scene.control.ScrollPane.ScrollBarPolicy; + import javafx.scene.control.SkinBase; import javafx.scene.input.MouseEvent; import javafx.scene.input.ScrollEvent; import javafx.scene.input.TouchEvent; import javafx.scene.layout.StackPane; import javafx.scene.shape.Rectangle; import javafx.util.Duration; import com.sun.javafx.util.Utils; import com.sun.javafx.scene.control.behavior.ScrollPaneBehavior; import static com.sun.javafx.scene.control.skin.Utils.*; import javafx.geometry.Insets; ! import java.util.function.Consumer; ! ! /** ! * Default skin implementation for the {@link ScrollPane} control. ! * ! * @see ScrollPane ! * @since 9 ! */ ! public class ScrollPaneSkin extends SkinBase<ScrollPane> { /*************************************************************************** * * ! * Static fields * * * **************************************************************************/ private static final double DEFAULT_PREF_SIZE = 100.0;
*** 73,85 **** --- 85,106 ---- private static final double DEFAULT_SB_BREADTH = 12.0; private static final double DEFAULT_EMBEDDED_SB_BREADTH = 8.0; private static final double PAN_THRESHOLD = 0.5; + + + /*************************************************************************** + * * + * Private fields * + * * + **************************************************************************/ + // state from the control private Node scrollNode; + private final BehaviorBase<ScrollPane> behavior; private double nodeWidth; private double nodeHeight; private boolean nodeSizeInvalid = true;
*** 98,109 **** private StackPane viewRect; private StackPane viewContent; private double contentWidth; private double contentHeight; private StackPane corner; ! protected ScrollBar hsb; ! protected ScrollBar vsb; double pressX; double pressY; double ohvalue; double ovvalue; --- 119,130 ---- private StackPane viewRect; private StackPane viewContent; private double contentWidth; private double contentHeight; private StackPane corner; ! ScrollBar hsb; ! ScrollBar vsb; double pressX; double pressY; double ohvalue; double ovvalue;
*** 112,148 **** private boolean touchDetected = false; private boolean mouseDown = false; Rectangle clipRect; /*************************************************************************** * * ! * Constructors * * * **************************************************************************/ - public ScrollPaneSkin(final ScrollPane scrollpane) { - super(scrollpane, new ScrollPaneBehavior(scrollpane)); - initialize(); - // Register listeners - registerChangeListener(scrollpane.contentProperty(), "NODE"); - registerChangeListener(scrollpane.fitToWidthProperty(), "FIT_TO_WIDTH"); - registerChangeListener(scrollpane.fitToHeightProperty(), "FIT_TO_HEIGHT"); - registerChangeListener(scrollpane.hbarPolicyProperty(), "HBAR_POLICY"); - registerChangeListener(scrollpane.vbarPolicyProperty(), "VBAR_POLICY"); - registerChangeListener(scrollpane.hvalueProperty(), "HVALUE"); - registerChangeListener(scrollpane.hmaxProperty(), "HMAX"); - registerChangeListener(scrollpane.hminProperty(), "HMIN"); - registerChangeListener(scrollpane.vvalueProperty(), "VVALUE"); - registerChangeListener(scrollpane.vmaxProperty(), "VMAX"); - registerChangeListener(scrollpane.vminProperty(), "VMIN"); - registerChangeListener(scrollpane.prefViewportWidthProperty(), "VIEWPORT_SIZE_HINT"); - registerChangeListener(scrollpane.prefViewportHeightProperty(), "VIEWPORT_SIZE_HINT"); - registerChangeListener(scrollpane.minViewportWidthProperty(), "VIEWPORT_SIZE_HINT"); - registerChangeListener(scrollpane.minViewportHeightProperty(), "VIEWPORT_SIZE_HINT"); - } - private final InvalidationListener nodeListener = new InvalidationListener() { @Override public void invalidated(Observable valueModel) { if (!nodeSizeInvalid) { final Bounds scrollNodeBounds = scrollNode.getLayoutBounds(); final double scrollNodeWidth = scrollNodeBounds.getWidth(); --- 133,160 ---- private boolean touchDetected = false; private boolean mouseDown = false; Rectangle clipRect; + Timeline sbTouchTimeline; + KeyFrame sbTouchKF1; + KeyFrame sbTouchKF2; + Timeline contentsToViewTimeline; + KeyFrame contentsToViewKF1; + KeyFrame contentsToViewKF2; + KeyFrame contentsToViewKF3; + + private boolean tempVisibility; + + + /*************************************************************************** * * ! * Listeners * * * **************************************************************************/ private final InvalidationListener nodeListener = new InvalidationListener() { @Override public void invalidated(Observable valueModel) { if (!nodeSizeInvalid) { final Bounds scrollNodeBounds = scrollNode.getLayoutBounds(); final double scrollNodeWidth = scrollNodeBounds.getWidth();
*** 226,258 **** } } } }; private void initialize() { // requestLayout calls below should not trigger requestLayout above ScrollPane // setManaged(false); ScrollPane control = getSkinnable(); scrollNode = control.getContent(); ParentTraversalEngine traversalEngine = new ParentTraversalEngine(getSkinnable()); ! traversalEngine.addTraverseListener(this); getSkinnable().setImpl_traversalEngine(traversalEngine); if (scrollNode != null) { scrollNode.layoutBoundsProperty().addListener(nodeListener); scrollNode.layoutBoundsProperty().addListener(boundsChangeListener); } viewRect = new StackPane() { ! ! @Override ! protected void layoutChildren() { viewContent.resize(getWidth(), getHeight()); } - }; // prevent requestLayout requests from within scrollNode from percolating up viewRect.setManaged(false); viewRect.setCache(true); viewRect.getStyleClass().add("viewport"); --- 238,646 ---- } } } }; + + + /*************************************************************************** + * * + * Constructors * + * * + **************************************************************************/ + + /** + * Creates a new ScrollPaneSkin instance, installing the necessary child + * nodes into the Control {@link Control#getChildren() children} list, as + * well as the necessary input mappings for handling key, mouse, etc events. + * + * @param control The control that this skin should be installed onto. + */ + public ScrollPaneSkin(final ScrollPane control) { + super(control); + + // install default input map for the ScrollPane control + behavior = new ScrollPaneBehavior(control); + // control.setInputMap(behavior.getInputMap()); + + initialize(); + + // Register listeners + Consumer<ObservableValue<?>> viewportSizeHintConsumer = e -> { + // change affects pref size, so requestLayout on control + getSkinnable().requestLayout(); + }; + registerChangeListener(control.contentProperty(), e -> { + if (scrollNode != getSkinnable().getContent()) { + if (scrollNode != null) { + scrollNode.layoutBoundsProperty().removeListener(nodeListener); + scrollNode.layoutBoundsProperty().removeListener(boundsChangeListener); + viewContent.getChildren().remove(scrollNode); + } + scrollNode = getSkinnable().getContent(); + if (scrollNode != null) { + nodeWidth = snapSize(scrollNode.getLayoutBounds().getWidth()); + nodeHeight = snapSize(scrollNode.getLayoutBounds().getHeight()); + viewContent.getChildren().setAll(scrollNode); + scrollNode.layoutBoundsProperty().addListener(nodeListener); + scrollNode.layoutBoundsProperty().addListener(boundsChangeListener); + } + } + getSkinnable().requestLayout(); + }); + registerChangeListener(control.fitToWidthProperty(), e -> { + getSkinnable().requestLayout(); + viewRect.requestLayout(); + }); + registerChangeListener(control.fitToHeightProperty(), e -> { + getSkinnable().requestLayout(); + viewRect.requestLayout(); + }); + registerChangeListener(control.hbarPolicyProperty(), e -> { + // change might affect pref size, so requestLayout on control + getSkinnable().requestLayout(); + }); + registerChangeListener(control.vbarPolicyProperty(), e -> { + // change might affect pref size, so requestLayout on control + getSkinnable().requestLayout(); + }); + registerChangeListener(control.hvalueProperty(), e -> hsb.setValue(getSkinnable().getHvalue())); + registerChangeListener(control.hmaxProperty(), e -> hsb.setMax(getSkinnable().getHmax())); + registerChangeListener(control.hminProperty(), e -> hsb.setMin(getSkinnable().getHmin())); + registerChangeListener(control.vvalueProperty(), e -> vsb.setValue(getSkinnable().getVvalue())); + registerChangeListener(control.vmaxProperty(), e -> vsb.setMax(getSkinnable().getVmax())); + registerChangeListener(control.vminProperty(), e -> vsb.setMin(getSkinnable().getVmin())); + registerChangeListener(control.prefViewportWidthProperty(), viewportSizeHintConsumer); + registerChangeListener(control.prefViewportHeightProperty(), viewportSizeHintConsumer); + registerChangeListener(control.minViewportWidthProperty(), viewportSizeHintConsumer); + registerChangeListener(control.minViewportHeightProperty(), viewportSizeHintConsumer); + } + + + + /*************************************************************************** + * * + * Properties * + * * + **************************************************************************/ + + private DoubleProperty contentPosX; + private final void setContentPosX(double value) { contentPosXProperty().set(value); } + private final double getContentPosX() { return contentPosX == null ? 0.0 : contentPosX.get(); } + private final DoubleProperty contentPosXProperty() { + if (contentPosX == null) { + contentPosX = new DoublePropertyBase() { + @Override protected void invalidated() { + hsb.setValue(getContentPosX()); + getSkinnable().requestLayout(); + } + + @Override + public Object getBean() { + return ScrollPaneSkin.this; + } + + @Override + public String getName() { + return "contentPosX"; + } + }; + } + return contentPosX; + } + + private DoubleProperty contentPosY; + private final void setContentPosY(double value) { contentPosYProperty().set(value); } + private final double getContentPosY() { return contentPosY == null ? 0.0 : contentPosY.get(); } + private final DoubleProperty contentPosYProperty() { + if (contentPosY == null) { + contentPosY = new DoublePropertyBase() { + @Override protected void invalidated() { + vsb.setValue(getContentPosY()); + getSkinnable().requestLayout(); + } + + @Override + public Object getBean() { + return ScrollPaneSkin.this; + } + + @Override + public String getName() { + return "contentPosY"; + } + }; + } + return contentPosY; + } + + + + /*************************************************************************** + * * + * Public API * + * * + **************************************************************************/ + + /** {@inheritDoc} */ + @Override public void dispose() { + super.dispose(); + + if (behavior != null) { + behavior.dispose(); + } + } + + /** + * Returns the horizontal {@link ScrollBar} used in this ScrollPaneSkin + * instance. + */ + public final ScrollBar getHorizontalScrollBar() { + return hsb; + } + + /** + * Returns the vertical {@link ScrollBar} used in this ScrollPaneSkin + * instance. + */ + public final ScrollBar getVerticalScrollBar() { + return vsb; + } + + /** {@inheritDoc} */ + @Override protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) { + final ScrollPane sp = getSkinnable(); + + double vsbWidth = computeVsbSizeHint(sp); + double minWidth = vsbWidth + snappedLeftInset() + snappedRightInset(); + + if (sp.getPrefViewportWidth() > 0) { + return (sp.getPrefViewportWidth() + minWidth); + } + else if (sp.getContent() != null) { + return (sp.getContent().prefWidth(height) + minWidth); + } + else { + return Math.max(minWidth, DEFAULT_PREF_SIZE); + } + } + + /** {@inheritDoc} */ + @Override protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) { + final ScrollPane sp = getSkinnable(); + + double hsbHeight = computeHsbSizeHint(sp); + double minHeight = hsbHeight + snappedTopInset() + snappedBottomInset(); + + if (sp.getPrefViewportHeight() > 0) { + return (sp.getPrefViewportHeight() + minHeight); + } + else if (sp.getContent() != null) { + return (sp.getContent().prefHeight(width) + minHeight); + } + else { + return Math.max(minHeight, DEFAULT_PREF_SIZE); + } + } + + /** {@inheritDoc} */ + @Override protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) { + final ScrollPane sp = getSkinnable(); + + double vsbWidth = computeVsbSizeHint(sp); + double minWidth = vsbWidth + snappedLeftInset() + snappedRightInset(); + + if (sp.getMinViewportWidth() > 0) { + return (sp.getMinViewportWidth() + minWidth); + } else { + double w = corner.minWidth(-1); + return (w > 0) ? (3 * w) : (DEFAULT_MIN_SIZE); + } + + } + + /** {@inheritDoc} */ + @Override protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) { + final ScrollPane sp = getSkinnable(); + + double hsbHeight = computeHsbSizeHint(sp); + double minHeight = hsbHeight + snappedTopInset() + snappedBottomInset(); + + if (sp.getMinViewportHeight() > 0) { + return (sp.getMinViewportHeight() + minHeight); + } else { + double h = corner.minHeight(-1); + return (h > 0) ? (3 * h) : (DEFAULT_MIN_SIZE); + } + } + + @Override protected void layoutChildren(final double x, final double y, + final double w, final double h) { + final ScrollPane control = getSkinnable(); + final Insets padding = control.getPadding(); + final double rightPadding = snapSize(padding.getRight()); + final double leftPadding = snapSize(padding.getLeft()); + final double topPadding = snapSize(padding.getTop()); + final double bottomPadding = snapSize(padding.getBottom()); + + vsb.setMin(control.getVmin()); + vsb.setMax(control.getVmax()); + + //should only do this on css setup + hsb.setMin(control.getHmin()); + hsb.setMax(control.getHmax()); + + contentWidth = w; + contentHeight = h; + + /* + ** we want the scrollbars to go right to the border + */ + double hsbWidth = 0; + double vsbHeight = 0; + + computeScrollNodeSize(contentWidth, contentHeight); + computeScrollBarSize(); + + for (int i = 0; i < 2; ++i) { + vsbvis = determineVerticalSBVisible(); + hsbvis = determineHorizontalSBVisible(); + + if (vsbvis && !Properties.IS_TOUCH_SUPPORTED) { + contentWidth = w - vsbWidth; + } + hsbWidth = w + leftPadding + rightPadding - (vsbvis ? vsbWidth : 0); + if (hsbvis && !Properties.IS_TOUCH_SUPPORTED) { + contentHeight = h - hsbHeight; + } + vsbHeight = h + topPadding + bottomPadding - (hsbvis ? hsbHeight : 0); + } + + + if (scrollNode != null && scrollNode.isResizable()) { + // maybe adjust size now that scrollbars may take up space + if (vsbvis && hsbvis) { + // adjust just once to accommodate + computeScrollNodeSize(contentWidth, contentHeight); + + } else if (hsbvis && !vsbvis) { + computeScrollNodeSize(contentWidth, contentHeight); + vsbvis = determineVerticalSBVisible(); + if (vsbvis) { + // now both are visible + contentWidth -= vsbWidth; + hsbWidth -= vsbWidth; + computeScrollNodeSize(contentWidth, contentHeight); + } + } else if (vsbvis && !hsbvis) { + computeScrollNodeSize(contentWidth, contentHeight); + hsbvis = determineHorizontalSBVisible(); + if (hsbvis) { + // now both are visible + contentHeight -= hsbHeight; + vsbHeight -= hsbHeight; + computeScrollNodeSize(contentWidth, contentHeight); + } + } + } + + // figure out the content area that is to be filled + double cx = snappedLeftInset() - leftPadding; + double cy = snappedTopInset() - topPadding; + + vsb.setVisible(vsbvis); + if (vsbvis) { + /* + ** round up position of ScrollBar, round down it's size. + ** + ** Positioning the ScrollBar + ** The Padding should go between the content and the edge, + ** otherwise changes in padding move the ScrollBar, and could + ** in extreme cases size the ScrollBar to become unusable. + ** The -1, +1 plus one bit : + ** If padding in => 1 then we allow one pixel to appear as the + ** outside border of the Scrollbar, and the rest on the inside. + ** If padding is < 1 then we just stick to the edge. + */ + vsb.resizeRelocate(snappedLeftInset() + w - vsbWidth + (rightPadding < 1 ? 0 : rightPadding - 1) , + cy, vsbWidth, vsbHeight); + } + updateVerticalSB(); + + hsb.setVisible(hsbvis); + if (hsbvis) { + /* + ** round up position of ScrollBar, round down it's size. + ** + ** Positioning the ScrollBar + ** The Padding should go between the content and the edge, + ** otherwise changes in padding move the ScrollBar, and could + ** in extreme cases size the ScrollBar to become unusable. + ** The -1, +1 plus one bit : + ** If padding in => 1 then we allow one pixel to appear as the + ** outside border of the Scrollbar, and the rest on the inside. + ** If padding is < 1 then we just stick to the edge. + */ + hsb.resizeRelocate(cx, snappedTopInset() + h - hsbHeight + (bottomPadding < 1 ? 0 : bottomPadding - 1), + hsbWidth, hsbHeight); + } + updateHorizontalSB(); + + viewRect.resizeRelocate(snappedLeftInset(), snappedTopInset(), snapSize(contentWidth), snapSize(contentHeight)); + resetClip(); + + if (vsbvis && hsbvis) { + corner.setVisible(true); + double cornerWidth = vsbWidth; + double cornerHeight = hsbHeight; + corner.resizeRelocate(snapPosition(vsb.getLayoutX()), snapPosition(hsb.getLayoutY()), snapSize(cornerWidth), snapSize(cornerHeight)); + } else { + corner.setVisible(false); + } + control.setViewportBounds(new BoundingBox(snapPosition(viewContent.getLayoutX()), snapPosition(viewContent.getLayoutY()), snapSize(contentWidth), snapSize(contentHeight))); + } + + /** {@inheritDoc} */ + @Override protected Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) { + switch (attribute) { + case VERTICAL_SCROLLBAR: return vsb; + case HORIZONTAL_SCROLLBAR: return hsb; + default: return super.queryAccessibleAttribute(attribute, parameters); + } + } + + + + /*************************************************************************** + * * + * Private implementation * + * * + **************************************************************************/ + private void initialize() { // requestLayout calls below should not trigger requestLayout above ScrollPane // setManaged(false); ScrollPane control = getSkinnable(); scrollNode = control.getContent(); ParentTraversalEngine traversalEngine = new ParentTraversalEngine(getSkinnable()); ! traversalEngine.addTraverseListener((node, bounds) -> { ! // auto-scroll so node is within (0,0),(contentWidth,contentHeight) ! scrollBoundsIntoView(bounds); ! }); getSkinnable().setImpl_traversalEngine(traversalEngine); if (scrollNode != null) { scrollNode.layoutBoundsProperty().addListener(nodeListener); scrollNode.layoutBoundsProperty().addListener(boundsChangeListener); } viewRect = new StackPane() { ! @Override protected void layoutChildren() { viewContent.resize(getWidth(), getHeight()); } }; // prevent requestLayout requests from within scrollNode from percolating up viewRect.setManaged(false); viewRect.setCache(true); viewRect.getStyleClass().add("viewport");
*** 313,334 **** /* ** listeners, and assorted housekeeping */ InvalidationListener vsbListener = valueModel -> { ! if (!IS_TOUCH_SUPPORTED) { posY = Utils.clamp(getSkinnable().getVmin(), vsb.getValue(), getSkinnable().getVmax()); } else { posY = vsb.getValue(); } updatePosY(); }; vsb.valueProperty().addListener(vsbListener); InvalidationListener hsbListener = valueModel -> { ! if (!IS_TOUCH_SUPPORTED) { posX = Utils.clamp(getSkinnable().getHmin(), hsb.getValue(), getSkinnable().getHmax()); } else { posX = hsb.getValue(); } --- 701,722 ---- /* ** listeners, and assorted housekeeping */ InvalidationListener vsbListener = valueModel -> { ! if (!Properties.IS_TOUCH_SUPPORTED) { posY = Utils.clamp(getSkinnable().getVmin(), vsb.getValue(), getSkinnable().getVmax()); } else { posY = vsb.getValue(); } updatePosY(); }; vsb.valueProperty().addListener(vsbListener); InvalidationListener hsbListener = valueModel -> { ! if (!Properties.IS_TOUCH_SUPPORTED) { posX = Utils.clamp(getSkinnable().getHmin(), hsb.getValue(), getSkinnable().getHmax()); } else { posX = hsb.getValue(); }
*** 336,357 **** }; hsb.valueProperty().addListener(hsbListener); viewRect.setOnMousePressed(e -> { mouseDown = true; ! if (IS_TOUCH_SUPPORTED) { startSBReleasedAnimation(); } pressX = e.getX(); pressY = e.getY(); ohvalue = hsb.getValue(); ovvalue = vsb.getValue(); }); viewRect.setOnDragDetected(e -> { ! if (IS_TOUCH_SUPPORTED) { startSBReleasedAnimation(); } if (getSkinnable().isPannable()) { dragDetected = true; if (saveCursor == null) { --- 724,745 ---- }; hsb.valueProperty().addListener(hsbListener); viewRect.setOnMousePressed(e -> { mouseDown = true; ! if (Properties.IS_TOUCH_SUPPORTED) { startSBReleasedAnimation(); } pressX = e.getX(); pressY = e.getY(); ohvalue = hsb.getValue(); ovvalue = vsb.getValue(); }); viewRect.setOnDragDetected(e -> { ! if (Properties.IS_TOUCH_SUPPORTED) { startSBReleasedAnimation(); } if (getSkinnable().isPannable()) { dragDetected = true; if (saveCursor == null) {
*** 384,400 **** posX > getSkinnable().getHmax() || posX < getSkinnable().getHmin()) && !touchDetected) { startContentsToViewport(); } }); viewRect.setOnMouseDragged(e -> { ! if (IS_TOUCH_SUPPORTED) { startSBReleasedAnimation(); } /* ** for mobile-touch we allow drag, even if not pannagle */ ! if (getSkinnable().isPannable() || IS_TOUCH_SUPPORTED) { double deltaX = pressX - e.getX(); double deltaY = pressY - e.getY(); /* ** we only drag if not all of the content is visible. */ --- 772,788 ---- posX > getSkinnable().getHmax() || posX < getSkinnable().getHmin()) && !touchDetected) { startContentsToViewport(); } }); viewRect.setOnMouseDragged(e -> { ! if (Properties.IS_TOUCH_SUPPORTED) { startSBReleasedAnimation(); } /* ** for mobile-touch we allow drag, even if not pannagle */ ! if (getSkinnable().isPannable() || Properties.IS_TOUCH_SUPPORTED) { double deltaX = pressX - e.getX(); double deltaY = pressY - e.getY(); /* ** we only drag if not all of the content is visible. */
*** 402,412 **** if (Math.abs(deltaX) > PAN_THRESHOLD) { if (isReverseNodeOrientation()) { deltaX = -deltaX; } double newHVal = (ohvalue + deltaX / (nodeWidth - viewRect.getWidth()) * (hsb.getMax() - hsb.getMin())); ! if (!IS_TOUCH_SUPPORTED) { if (newHVal > hsb.getMax()) { newHVal = hsb.getMax(); } else if (newHVal < hsb.getMin()) { newHVal = hsb.getMin(); --- 790,800 ---- if (Math.abs(deltaX) > PAN_THRESHOLD) { if (isReverseNodeOrientation()) { deltaX = -deltaX; } double newHVal = (ohvalue + deltaX / (nodeWidth - viewRect.getWidth()) * (hsb.getMax() - hsb.getMin())); ! if (!Properties.IS_TOUCH_SUPPORTED) { if (newHVal > hsb.getMax()) { newHVal = hsb.getMax(); } else if (newHVal < hsb.getMin()) { newHVal = hsb.getMin();
*** 422,432 **** ** we only drag if not all of the content is visible. */ if (vsb.getVisibleAmount() > 0.0 && vsb.getVisibleAmount() < vsb.getMax()) { if (Math.abs(deltaY) > PAN_THRESHOLD) { double newVVal = (ovvalue + deltaY / (nodeHeight - viewRect.getHeight()) * (vsb.getMax() - vsb.getMin())); ! if (!IS_TOUCH_SUPPORTED) { if (newVVal > vsb.getMax()) { newVVal = vsb.getMax(); } else if (newVVal < vsb.getMin()) { newVVal = vsb.getMin(); --- 810,820 ---- ** we only drag if not all of the content is visible. */ if (vsb.getVisibleAmount() > 0.0 && vsb.getVisibleAmount() < vsb.getMax()) { if (Math.abs(deltaY) > PAN_THRESHOLD) { double newVVal = (ovvalue + deltaY / (nodeHeight - viewRect.getHeight()) * (vsb.getMax() - vsb.getMin())); ! if (!Properties.IS_TOUCH_SUPPORTED) { if (newVVal > vsb.getMax()) { newVVal = vsb.getMax(); } else if (newVVal < vsb.getMin()) { newVVal = vsb.getMin();
*** 487,497 **** * allows for us to prioritise handling (and consuming) the event * internally, before it is made available to users listening to events * on the control. This is consistent with the VirtualFlow-based controls. */ viewRect.addEventHandler(ScrollEvent.SCROLL, event -> { ! if (IS_TOUCH_SUPPORTED) { startSBReleasedAnimation(); } /* ** if we're completely visible then do nothing.... ** we only consume an event that we've used. --- 875,885 ---- * allows for us to prioritise handling (and consuming) the event * internally, before it is made available to users listening to events * on the control. This is consistent with the VirtualFlow-based controls. */ viewRect.addEventHandler(ScrollEvent.SCROLL, event -> { ! if (Properties.IS_TOUCH_SUPPORTED) { startSBReleasedAnimation(); } /* ** if we're completely visible then do nothing.... ** we only consume an event that we've used.
*** 504,514 **** } else { vPixelValue = 0.0; } double newValue = vsb.getValue()+(-event.getDeltaY())*vPixelValue; ! if (!IS_TOUCH_SUPPORTED) { if ((event.getDeltaY() > 0.0 && vsb.getValue() > vsb.getMin()) || (event.getDeltaY() < 0.0 && vsb.getValue() < vsb.getMax())) { vsb.setValue(newValue); event.consume(); } --- 892,902 ---- } else { vPixelValue = 0.0; } double newValue = vsb.getValue()+(-event.getDeltaY())*vPixelValue; ! if (!Properties.IS_TOUCH_SUPPORTED) { if ((event.getDeltaY() > 0.0 && vsb.getValue() > vsb.getMin()) || (event.getDeltaY() < 0.0 && vsb.getValue() < vsb.getMax())) { vsb.setValue(newValue); event.consume(); }
*** 537,547 **** else { hPixelValue = 0.0; } double newValue = hsb.getValue()+(-event.getDeltaX())*hPixelValue; ! if (!IS_TOUCH_SUPPORTED) { if ((event.getDeltaX() > 0.0 && hsb.getValue() > hsb.getMin()) || (event.getDeltaX() < 0.0 && hsb.getValue() < hsb.getMax())) { hsb.setValue(newValue); event.consume(); } --- 925,935 ---- else { hPixelValue = 0.0; } double newValue = hsb.getValue()+(-event.getDeltaX())*hPixelValue; ! if (!Properties.IS_TOUCH_SUPPORTED) { if ((event.getDeltaX() > 0.0 && hsb.getValue() > hsb.getMin()) || (event.getDeltaX() < 0.0 && hsb.getValue() < hsb.getMax())) { hsb.setValue(newValue); event.consume(); }
*** 574,778 **** }); getSkinnable().addEventHandler(TouchEvent.TOUCH_RELEASED, e -> { touchDetected = false; e.consume(); ! }); ! ! // ScrollPanes do not block all MouseEvents by default, unlike most other UI Controls. ! consumeMouseEvents(false); ! ! // update skin initial state to match control (see RT-35554) ! hsb.setValue(control.getHvalue()); ! vsb.setValue(control.getVvalue()); ! } ! ! ! @Override protected void handleControlPropertyChanged(String p) { ! super.handleControlPropertyChanged(p); ! if ("NODE".equals(p)) { ! if (scrollNode != getSkinnable().getContent()) { ! if (scrollNode != null) { ! scrollNode.layoutBoundsProperty().removeListener(nodeListener); ! scrollNode.layoutBoundsProperty().removeListener(boundsChangeListener); ! viewContent.getChildren().remove(scrollNode); ! } ! scrollNode = getSkinnable().getContent(); ! if (scrollNode != null) { ! nodeWidth = snapSize(scrollNode.getLayoutBounds().getWidth()); ! nodeHeight = snapSize(scrollNode.getLayoutBounds().getHeight()); ! viewContent.getChildren().setAll(scrollNode); ! scrollNode.layoutBoundsProperty().addListener(nodeListener); ! scrollNode.layoutBoundsProperty().addListener(boundsChangeListener); ! } ! } ! getSkinnable().requestLayout(); ! } else if ("FIT_TO_WIDTH".equals(p) || "FIT_TO_HEIGHT".equals(p)) { ! getSkinnable().requestLayout(); ! viewRect.requestLayout(); ! } else if ("HBAR_POLICY".equals(p) || "VBAR_POLICY".equals(p)) { ! // change might affect pref size, so requestLayout on control ! getSkinnable().requestLayout(); ! } else if ("HVALUE".equals(p)) { ! hsb.setValue(getSkinnable().getHvalue()); ! } else if ("HMAX".equals(p)) { ! hsb.setMax(getSkinnable().getHmax()); ! } else if ("HMIN".equals(p)) { ! hsb.setMin(getSkinnable().getHmin()); ! } else if ("VVALUE".equals(p)) { ! vsb.setValue(getSkinnable().getVvalue()); ! } else if ("VMAX".equals(p)) { ! vsb.setMax(getSkinnable().getVmax()); ! } else if ("VMIN".equals(p)) { ! vsb.setMin(getSkinnable().getVmin()); ! } else if ("VIEWPORT_SIZE_HINT".equals(p)) { ! // change affects pref size, so requestLayout on control ! getSkinnable().requestLayout(); ! } ! } ! ! void scrollBoundsIntoView(Bounds b) { ! double dx = 0.0; ! double dy = 0.0; ! if (b.getMaxX() > contentWidth) { ! dx = b.getMinX() - snappedLeftInset(); ! } ! if (b.getMinX() < snappedLeftInset()) { ! dx = b.getMaxX() - contentWidth - snappedLeftInset(); ! } ! if (b.getMaxY() > snappedTopInset() + contentHeight) { ! dy = b.getMinY() - snappedTopInset(); ! } ! if (b.getMinY() < snappedTopInset()) { ! dy = b.getMaxY() - contentHeight - snappedTopInset(); ! } ! // We want to move contentPanel's layoutX,Y by (dx,dy). ! // But to do this we have to set the scrollbars' values appropriately. ! ! if (dx != 0) { ! double sdx = dx * (hsb.getMax() - hsb.getMin()) / (nodeWidth - contentWidth); ! // Adjust back for some amount so that the Node border is not too close to view border ! sdx += -1 * Math.signum(sdx) * hsb.getUnitIncrement() / 5; // This accounts to 2% of view width ! hsb.setValue(hsb.getValue() + sdx); ! getSkinnable().requestLayout(); ! } ! if (dy != 0) { ! double sdy = dy * (vsb.getMax() - vsb.getMin()) / (nodeHeight - contentHeight); ! // Adjust back for some amount so that the Node border is not too close to view border ! sdy += -1 * Math.signum(sdy) * vsb.getUnitIncrement() / 5; // This accounts to 2% of view height ! vsb.setValue(vsb.getValue() + sdy); ! getSkinnable().requestLayout(); ! } ! ! } ! ! /* ! ** auto-scroll so node is within (0,0),(contentWidth,contentHeight) ! */ ! @Override public void onTraverse(Node n, Bounds b) { ! scrollBoundsIntoView(b); ! } ! ! public void hsbIncrement() { ! if (hsb != null) hsb.increment(); ! } ! public void hsbDecrement() { ! if (hsb != null) hsb.decrement(); ! } ! ! // TODO: add page increment and decrement ! public void hsbPageIncrement() { ! if (hsb != null) hsb.increment(); ! } ! // TODO: add page increment and decrement ! public void hsbPageDecrement() { ! if (hsb != null) hsb.decrement(); ! } ! ! public void vsbIncrement() { ! if (vsb != null) vsb.increment(); ! } ! public void vsbDecrement() { ! if (vsb != null) vsb.decrement(); ! } ! ! // TODO: add page increment and decrement ! public void vsbPageIncrement() { ! if (vsb != null) vsb.increment(); ! } ! // TODO: add page increment and decrement ! public void vsbPageDecrement() { ! if (vsb != null) vsb.decrement(); ! } ! ! /*************************************************************************** ! * * ! * Layout * ! * * ! **************************************************************************/ ! ! @Override protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) { ! final ScrollPane sp = getSkinnable(); ! ! double vsbWidth = computeVsbSizeHint(sp); ! double minWidth = vsbWidth + snappedLeftInset() + snappedRightInset(); ! ! if (sp.getPrefViewportWidth() > 0) { ! return (sp.getPrefViewportWidth() + minWidth); ! } ! else if (sp.getContent() != null) { ! return (sp.getContent().prefWidth(height) + minWidth); ! } ! else { ! return Math.max(minWidth, DEFAULT_PREF_SIZE); ! } ! } ! @Override protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) { ! final ScrollPane sp = getSkinnable(); ! double hsbHeight = computeHsbSizeHint(sp); ! double minHeight = hsbHeight + snappedTopInset() + snappedBottomInset(); ! if (sp.getPrefViewportHeight() > 0) { ! return (sp.getPrefViewportHeight() + minHeight); } ! else if (sp.getContent() != null) { ! return (sp.getContent().prefHeight(width) + minHeight); } ! else { ! return Math.max(minHeight, DEFAULT_PREF_SIZE); } } ! @Override protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) { ! final ScrollPane sp = getSkinnable(); ! ! double vsbWidth = computeVsbSizeHint(sp); ! double minWidth = vsbWidth + snappedLeftInset() + snappedRightInset(); ! ! if (sp.getMinViewportWidth() > 0) { ! return (sp.getMinViewportWidth() + minWidth); ! } else { ! double w = corner.minWidth(-1); ! return (w > 0) ? (3 * w) : (DEFAULT_MIN_SIZE); } ! } - @Override protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) { - final ScrollPane sp = getSkinnable(); - - double hsbHeight = computeHsbSizeHint(sp); - double minHeight = hsbHeight + snappedTopInset() + snappedBottomInset(); - - if (sp.getMinViewportHeight() > 0) { - return (sp.getMinViewportHeight() + minHeight); - } else { - double h = corner.minHeight(-1); - return (h > 0) ? (3 * h) : (DEFAULT_MIN_SIZE); - } } /** * Computes the size that should be reserved for horizontal scrollbar in size hints (min/pref height) */ --- 962,1014 ---- }); getSkinnable().addEventHandler(TouchEvent.TOUCH_RELEASED, e -> { touchDetected = false; e.consume(); ! }); ! // ScrollPanes do not block all MouseEvents by default, unlike most other UI Controls. ! consumeMouseEvents(false); ! // update skin initial state to match control (see RT-35554) ! hsb.setValue(control.getHvalue()); ! vsb.setValue(control.getVvalue()); ! } ! void scrollBoundsIntoView(Bounds b) { ! double dx = 0.0; ! double dy = 0.0; ! if (b.getMaxX() > contentWidth) { ! dx = b.getMinX() - snappedLeftInset(); } ! if (b.getMinX() < snappedLeftInset()) { ! dx = b.getMaxX() - contentWidth - snappedLeftInset(); } ! if (b.getMaxY() > snappedTopInset() + contentHeight) { ! dy = b.getMinY() - snappedTopInset(); } + if (b.getMinY() < snappedTopInset()) { + dy = b.getMaxY() - contentHeight - snappedTopInset(); } + // We want to move contentPanel's layoutX,Y by (dx,dy). + // But to do this we have to set the scrollbars' values appropriately. ! if (dx != 0) { ! double sdx = dx * (hsb.getMax() - hsb.getMin()) / (nodeWidth - contentWidth); ! // Adjust back for some amount so that the Node border is not too close to view border ! sdx += -1 * Math.signum(sdx) * hsb.getUnitIncrement() / 5; // This accounts to 2% of view width ! hsb.setValue(hsb.getValue() + sdx); ! getSkinnable().requestLayout(); } ! if (dy != 0) { ! double sdy = dy * (vsb.getMax() - vsb.getMin()) / (nodeHeight - contentHeight); ! // Adjust back for some amount so that the Node border is not too close to view border ! sdy += -1 * Math.signum(sdy) * vsb.getUnitIncrement() / 5; // This accounts to 2% of view height ! vsb.setValue(vsb.getValue() + sdy); ! getSkinnable().requestLayout(); } } /** * Computes the size that should be reserved for horizontal scrollbar in size hints (min/pref height) */
*** 792,927 **** || sp.getMinViewportWidth() > 0))) ? vsb.prefWidth(ScrollBar.USE_COMPUTED_SIZE) : 0; } - @Override protected void layoutChildren(final double x, final double y, - final double w, final double h) { - final ScrollPane control = getSkinnable(); - final Insets padding = control.getPadding(); - final double rightPadding = snapSize(padding.getRight()); - final double leftPadding = snapSize(padding.getLeft()); - final double topPadding = snapSize(padding.getTop()); - final double bottomPadding = snapSize(padding.getBottom()); - - vsb.setMin(control.getVmin()); - vsb.setMax(control.getVmax()); - - //should only do this on css setup - hsb.setMin(control.getHmin()); - hsb.setMax(control.getHmax()); - - contentWidth = w; - contentHeight = h; - - /* - ** we want the scrollbars to go right to the border - */ - double hsbWidth = 0; - double vsbHeight = 0; - - computeScrollNodeSize(contentWidth, contentHeight); - computeScrollBarSize(); - - for (int i = 0; i < 2; ++i) { - vsbvis = determineVerticalSBVisible(); - hsbvis = determineHorizontalSBVisible(); - - if (vsbvis && !IS_TOUCH_SUPPORTED) { - contentWidth = w - vsbWidth; - } - hsbWidth = w + leftPadding + rightPadding - (vsbvis ? vsbWidth : 0); - if (hsbvis && !IS_TOUCH_SUPPORTED) { - contentHeight = h - hsbHeight; - } - vsbHeight = h + topPadding + bottomPadding - (hsbvis ? hsbHeight : 0); - } - - - if (scrollNode != null && scrollNode.isResizable()) { - // maybe adjust size now that scrollbars may take up space - if (vsbvis && hsbvis) { - // adjust just once to accommodate - computeScrollNodeSize(contentWidth, contentHeight); - - } else if (hsbvis && !vsbvis) { - computeScrollNodeSize(contentWidth, contentHeight); - vsbvis = determineVerticalSBVisible(); - if (vsbvis) { - // now both are visible - contentWidth -= vsbWidth; - hsbWidth -= vsbWidth; - computeScrollNodeSize(contentWidth, contentHeight); - } - } else if (vsbvis && !hsbvis) { - computeScrollNodeSize(contentWidth, contentHeight); - hsbvis = determineHorizontalSBVisible(); - if (hsbvis) { - // now both are visible - contentHeight -= hsbHeight; - vsbHeight -= hsbHeight; - computeScrollNodeSize(contentWidth, contentHeight); - } - } - } - - // figure out the content area that is to be filled - double cx = snappedLeftInset() - leftPadding; - double cy = snappedTopInset() - topPadding; - - vsb.setVisible(vsbvis); - if (vsbvis) { - /* - ** round up position of ScrollBar, round down it's size. - ** - ** Positioning the ScrollBar - ** The Padding should go between the content and the edge, - ** otherwise changes in padding move the ScrollBar, and could - ** in extreme cases size the ScrollBar to become unusable. - ** The -1, +1 plus one bit : - ** If padding in => 1 then we allow one pixel to appear as the - ** outside border of the Scrollbar, and the rest on the inside. - ** If padding is < 1 then we just stick to the edge. - */ - vsb.resizeRelocate(snappedLeftInset() + w - vsbWidth + (rightPadding < 1 ? 0 : rightPadding - 1) , - cy, vsbWidth, vsbHeight); - } - updateVerticalSB(); - - hsb.setVisible(hsbvis); - if (hsbvis) { - /* - ** round up position of ScrollBar, round down it's size. - ** - ** Positioning the ScrollBar - ** The Padding should go between the content and the edge, - ** otherwise changes in padding move the ScrollBar, and could - ** in extreme cases size the ScrollBar to become unusable. - ** The -1, +1 plus one bit : - ** If padding in => 1 then we allow one pixel to appear as the - ** outside border of the Scrollbar, and the rest on the inside. - ** If padding is < 1 then we just stick to the edge. - */ - hsb.resizeRelocate(cx, snappedTopInset() + h - hsbHeight + (bottomPadding < 1 ? 0 : bottomPadding - 1), - hsbWidth, hsbHeight); - } - updateHorizontalSB(); - - viewRect.resizeRelocate(snappedLeftInset(), snappedTopInset(), snapSize(contentWidth), snapSize(contentHeight)); - resetClip(); - - if (vsbvis && hsbvis) { - corner.setVisible(true); - double cornerWidth = vsbWidth; - double cornerHeight = hsbHeight; - corner.resizeRelocate(snapPosition(vsb.getLayoutX()), snapPosition(hsb.getLayoutY()), snapSize(cornerWidth), snapSize(cornerHeight)); - } else { - corner.setVisible(false); - } - control.setViewportBounds(new BoundingBox(snapPosition(viewContent.getLayoutX()), snapPosition(viewContent.getLayoutY()), snapSize(contentWidth), snapSize(contentHeight))); - } - private void computeScrollNodeSize(double contentWidth, double contentHeight) { if (scrollNode != null) { if (scrollNode.isResizable()) { ScrollPane control = getSkinnable(); Orientation bias = scrollNode.getContentBias(); --- 1028,1037 ----
*** 959,969 **** } private boolean determineHorizontalSBVisible() { final ScrollPane sp = getSkinnable(); ! if (IS_TOUCH_SUPPORTED) { return (tempVisibility && (nodeWidth > contentWidth)); } else { // RT-17395: ScrollBarPolicy might be null. If so, treat it as "AS_NEEDED", which is the default ScrollBarPolicy hbarPolicy = sp.getHbarPolicy(); --- 1069,1079 ---- } private boolean determineHorizontalSBVisible() { final ScrollPane sp = getSkinnable(); ! if (Properties.IS_TOUCH_SUPPORTED) { return (tempVisibility && (nodeWidth > contentWidth)); } else { // RT-17395: ScrollBarPolicy might be null. If so, treat it as "AS_NEEDED", which is the default ScrollBarPolicy hbarPolicy = sp.getHbarPolicy();
*** 975,985 **** } private boolean determineVerticalSBVisible() { final ScrollPane sp = getSkinnable(); ! if (IS_TOUCH_SUPPORTED) { return (tempVisibility && (nodeHeight > contentHeight)); } else { // RT-17395: ScrollBarPolicy might be null. If so, treat it as "AS_NEEDED", which is the default ScrollBarPolicy vbarPolicy = sp.getVbarPolicy(); --- 1085,1095 ---- } private boolean determineVerticalSBVisible() { final ScrollPane sp = getSkinnable(); ! if (Properties.IS_TOUCH_SUPPORTED) { return (tempVisibility && (nodeHeight > contentHeight)); } else { // RT-17395: ScrollBarPolicy might be null. If so, treat it as "AS_NEEDED", which is the default ScrollBarPolicy vbarPolicy = sp.getVbarPolicy();
*** 992,1012 **** private void computeScrollBarSize() { vsbWidth = snapSize(vsb.prefWidth(-1)); if (vsbWidth == 0) { // println("*** WARNING ScrollPaneSkin: can't get scroll bar width, using {DEFAULT_SB_BREADTH}"); ! if (IS_TOUCH_SUPPORTED) { vsbWidth = DEFAULT_EMBEDDED_SB_BREADTH; } else { vsbWidth = DEFAULT_SB_BREADTH; } } hsbHeight = snapSize(hsb.prefHeight(-1)); if (hsbHeight == 0) { // println("*** WARNING ScrollPaneSkin: can't get scroll bar height, using {DEFAULT_SB_BREADTH}"); ! if (IS_TOUCH_SUPPORTED) { hsbHeight = DEFAULT_EMBEDDED_SB_BREADTH; } else { hsbHeight = DEFAULT_SB_BREADTH; } --- 1102,1122 ---- private void computeScrollBarSize() { vsbWidth = snapSize(vsb.prefWidth(-1)); if (vsbWidth == 0) { // println("*** WARNING ScrollPaneSkin: can't get scroll bar width, using {DEFAULT_SB_BREADTH}"); ! if (Properties.IS_TOUCH_SUPPORTED) { vsbWidth = DEFAULT_EMBEDDED_SB_BREADTH; } else { vsbWidth = DEFAULT_SB_BREADTH; } } hsbHeight = snapSize(hsb.prefHeight(-1)); if (hsbHeight == 0) { // println("*** WARNING ScrollPaneSkin: can't get scroll bar height, using {DEFAULT_SB_BREADTH}"); ! if (Properties.IS_TOUCH_SUPPORTED) { hsbHeight = DEFAULT_EMBEDDED_SB_BREADTH; } else { hsbHeight = DEFAULT_SB_BREADTH; }
*** 1081,1102 **** private void resetClip() { clipRect.setWidth(snapSize(contentWidth)); clipRect.setHeight(snapSize(contentHeight)); } ! Timeline sbTouchTimeline; ! KeyFrame sbTouchKF1; ! KeyFrame sbTouchKF2; ! Timeline contentsToViewTimeline; ! KeyFrame contentsToViewKF1; ! KeyFrame contentsToViewKF2; ! KeyFrame contentsToViewKF3; ! ! private boolean tempVisibility; ! ! ! protected void startSBReleasedAnimation() { if (sbTouchTimeline == null) { /* ** timeline to leave the scrollbars visible for a short ** while after a scroll/drag */ --- 1191,1201 ---- private void resetClip() { clipRect.setWidth(snapSize(contentWidth)); clipRect.setHeight(snapSize(contentHeight)); } ! private void startSBReleasedAnimation() { if (sbTouchTimeline == null) { /* ** timeline to leave the scrollbars visible for a short ** while after a scroll/drag */
*** 1115,1127 **** sbTouchTimeline.getKeyFrames().addAll(sbTouchKF1, sbTouchKF2); } sbTouchTimeline.playFromStart(); } ! ! ! protected void startContentsToViewport() { double newPosX = posX; double newPosY = posY; setContentPosX(posX); setContentPosY(posY); --- 1214,1224 ---- sbTouchTimeline.getKeyFrames().addAll(sbTouchKF1, sbTouchKF2); } sbTouchTimeline.playFromStart(); } ! private void startContentsToViewport() { double newPosX = posX; double newPosY = posY; setContentPosX(posX); setContentPosY(posY);
*** 1139,1149 **** } else if (posX < getSkinnable().getHmin()) { newPosX = getSkinnable().getHmin(); } ! if (!IS_TOUCH_SUPPORTED) { startSBReleasedAnimation(); } /* ** timeline to return the contents of the scrollpane to the viewport --- 1236,1246 ---- } else if (posX < getSkinnable().getHmin()) { newPosX = getSkinnable().getHmin(); } ! if (!Properties.IS_TOUCH_SUPPORTED) { startSBReleasedAnimation(); } /* ** timeline to return the contents of the scrollpane to the viewport
*** 1171,1236 **** */ contentsToViewKF3 = new KeyFrame(Duration.millis(1500)); contentsToViewTimeline.getKeyFrames().addAll(contentsToViewKF1, contentsToViewKF2, contentsToViewKF3); contentsToViewTimeline.playFromStart(); } - - - private DoubleProperty contentPosX; - private void setContentPosX(double value) { contentPosXProperty().set(value); } - private double getContentPosX() { return contentPosX == null ? 0.0 : contentPosX.get(); } - private DoubleProperty contentPosXProperty() { - if (contentPosX == null) { - contentPosX = new DoublePropertyBase() { - @Override protected void invalidated() { - hsb.setValue(getContentPosX()); - getSkinnable().requestLayout(); - } - - @Override - public Object getBean() { - return ScrollPaneSkin.this; - } - - @Override - public String getName() { - return "contentPosX"; - } - }; - } - return contentPosX; - } - - private DoubleProperty contentPosY; - private void setContentPosY(double value) { contentPosYProperty().set(value); } - private double getContentPosY() { return contentPosY == null ? 0.0 : contentPosY.get(); } - private DoubleProperty contentPosYProperty() { - if (contentPosY == null) { - contentPosY = new DoublePropertyBase() { - @Override protected void invalidated() { - vsb.setValue(getContentPosY()); - getSkinnable().requestLayout(); - } - - @Override - public Object getBean() { - return ScrollPaneSkin.this; - } - - @Override - public String getName() { - return "contentPosY"; - } - }; - } - return contentPosY; - } - - @Override - protected Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) { - switch (attribute) { - case VERTICAL_SCROLLBAR: return vsb; - case HORIZONTAL_SCROLLBAR: return hsb; - default: return super.queryAccessibleAttribute(attribute, parameters); - } - } } --- 1268,1273 ----