183 } 184 } 185 }; 186 187 188 /* 189 ** The content of the ScrollPane has just changed bounds, check scrollBar positions. 190 */ 191 private final ChangeListener<Bounds> boundsChangeListener = new ChangeListener<Bounds>() { 192 @Override public void changed(ObservableValue<? extends Bounds> observable, Bounds oldBounds, Bounds newBounds) { 193 194 /* 195 ** For a height change then we want to reduce 196 ** viewport vertical jumping as much as possible. 197 ** We set a new vsb value to try to keep the same 198 ** content position at the top of the viewport 199 */ 200 double oldHeight = oldBounds.getHeight(); 201 double newHeight = newBounds.getHeight(); 202 if (oldHeight > 0 && oldHeight != newHeight) { 203 double oldPositionY = (snapPosition(snappedTopInset() - posY / (vsb.getMax() - vsb.getMin()) * (oldHeight - contentHeight))); 204 double newPositionY = (snapPosition(snappedTopInset() - posY / (vsb.getMax() - vsb.getMin()) * (newHeight - contentHeight))); 205 206 double newValueY = (oldPositionY/newPositionY)*vsb.getValue(); 207 if (newValueY < 0.0) { 208 vsb.setValue(0.0); 209 } 210 else if (newValueY < 1.0) { 211 vsb.setValue(newValueY); 212 } 213 else if (newValueY > 1.0) { 214 vsb.setValue(1.0); 215 } 216 } 217 218 /* 219 ** For a width change then we want to reduce 220 ** viewport horizontal jumping as much as possible. 221 ** We set a new hsb value to try to keep the same 222 ** content position to the left of the viewport 223 */ 224 double oldWidth = oldBounds.getWidth(); 225 double newWidth = newBounds.getWidth(); 226 if (oldWidth > 0 && oldWidth != newWidth) { 227 double oldPositionX = (snapPosition(snappedLeftInset() - posX / (hsb.getMax() - hsb.getMin()) * (oldWidth - contentWidth))); 228 double newPositionX = (snapPosition(snappedLeftInset() - posX / (hsb.getMax() - hsb.getMin()) * (newWidth - contentWidth))); 229 230 double newValueX = (oldPositionX/newPositionX)*hsb.getValue(); 231 if (newValueX < 0.0) { 232 hsb.setValue(0.0); 233 } 234 else if (newValueX < 1.0) { 235 hsb.setValue(newValueX); 236 } 237 else if (newValueX > 1.0) { 238 hsb.setValue(1.0); 239 } 240 } 241 } 242 }; 243 244 245 246 /*************************************************************************** 247 * * 248 * Constructors * 262 // install default input map for the ScrollPane control 263 behavior = new ScrollPaneBehavior(control); 264 // control.setInputMap(behavior.getInputMap()); 265 266 initialize(); 267 268 // Register listeners 269 Consumer<ObservableValue<?>> viewportSizeHintConsumer = e -> { 270 // change affects pref size, so requestLayout on control 271 getSkinnable().requestLayout(); 272 }; 273 registerChangeListener(control.contentProperty(), e -> { 274 if (scrollNode != getSkinnable().getContent()) { 275 if (scrollNode != null) { 276 scrollNode.layoutBoundsProperty().removeListener(nodeListener); 277 scrollNode.layoutBoundsProperty().removeListener(boundsChangeListener); 278 viewContent.getChildren().remove(scrollNode); 279 } 280 scrollNode = getSkinnable().getContent(); 281 if (scrollNode != null) { 282 nodeWidth = snapSize(scrollNode.getLayoutBounds().getWidth()); 283 nodeHeight = snapSize(scrollNode.getLayoutBounds().getHeight()); 284 viewContent.getChildren().setAll(scrollNode); 285 scrollNode.layoutBoundsProperty().addListener(nodeListener); 286 scrollNode.layoutBoundsProperty().addListener(boundsChangeListener); 287 } 288 } 289 getSkinnable().requestLayout(); 290 }); 291 registerChangeListener(control.fitToWidthProperty(), e -> { 292 getSkinnable().requestLayout(); 293 viewRect.requestLayout(); 294 }); 295 registerChangeListener(control.fitToHeightProperty(), e -> { 296 getSkinnable().requestLayout(); 297 viewRect.requestLayout(); 298 }); 299 registerChangeListener(control.hbarPolicyProperty(), e -> { 300 // change might affect pref size, so requestLayout on control 301 getSkinnable().requestLayout(); 302 }); 303 registerChangeListener(control.vbarPolicyProperty(), e -> { 461 462 /** {@inheritDoc} */ 463 @Override protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) { 464 final ScrollPane sp = getSkinnable(); 465 466 double hsbHeight = computeHsbSizeHint(sp); 467 double minHeight = hsbHeight + snappedTopInset() + snappedBottomInset(); 468 469 if (sp.getMinViewportHeight() > 0) { 470 return (sp.getMinViewportHeight() + minHeight); 471 } else { 472 double h = corner.minHeight(-1); 473 return (h > 0) ? (3 * h) : (DEFAULT_MIN_SIZE); 474 } 475 } 476 477 @Override protected void layoutChildren(final double x, final double y, 478 final double w, final double h) { 479 final ScrollPane control = getSkinnable(); 480 final Insets padding = control.getPadding(); 481 final double rightPadding = snapSize(padding.getRight()); 482 final double leftPadding = snapSize(padding.getLeft()); 483 final double topPadding = snapSize(padding.getTop()); 484 final double bottomPadding = snapSize(padding.getBottom()); 485 486 vsb.setMin(control.getVmin()); 487 vsb.setMax(control.getVmax()); 488 489 //should only do this on css setup 490 hsb.setMin(control.getHmin()); 491 hsb.setMax(control.getHmax()); 492 493 contentWidth = w; 494 contentHeight = h; 495 496 /* 497 ** we want the scrollbars to go right to the border 498 */ 499 double hsbWidth = 0; 500 double vsbHeight = 0; 501 502 computeScrollNodeSize(contentWidth, contentHeight); 503 computeScrollBarSize(); 504 569 570 hsb.setVisible(hsbvis); 571 if (hsbvis) { 572 /* 573 ** round up position of ScrollBar, round down it's size. 574 ** 575 ** Positioning the ScrollBar 576 ** The Padding should go between the content and the edge, 577 ** otherwise changes in padding move the ScrollBar, and could 578 ** in extreme cases size the ScrollBar to become unusable. 579 ** The -1, +1 plus one bit : 580 ** If padding in => 1 then we allow one pixel to appear as the 581 ** outside border of the Scrollbar, and the rest on the inside. 582 ** If padding is < 1 then we just stick to the edge. 583 */ 584 hsb.resizeRelocate(cx, snappedTopInset() + h - hsbHeight + (bottomPadding < 1 ? 0 : bottomPadding - 1), 585 hsbWidth, hsbHeight); 586 } 587 updateHorizontalSB(); 588 589 viewRect.resizeRelocate(snappedLeftInset(), snappedTopInset(), snapSize(contentWidth), snapSize(contentHeight)); 590 resetClip(); 591 592 if (vsbvis && hsbvis) { 593 corner.setVisible(true); 594 double cornerWidth = vsbWidth; 595 double cornerHeight = hsbHeight; 596 corner.resizeRelocate(snapPosition(vsb.getLayoutX()), snapPosition(hsb.getLayoutY()), snapSize(cornerWidth), snapSize(cornerHeight)); 597 } else { 598 corner.setVisible(false); 599 } 600 control.setViewportBounds(new BoundingBox(snapPosition(viewContent.getLayoutX()), snapPosition(viewContent.getLayoutY()), snapSize(contentWidth), snapSize(contentHeight))); 601 } 602 603 /** {@inheritDoc} */ 604 @Override protected Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) { 605 switch (attribute) { 606 case VERTICAL_SCROLLBAR: return vsb; 607 case HORIZONTAL_SCROLLBAR: return hsb; 608 default: return super.queryAccessibleAttribute(attribute, parameters); 609 } 610 } 611 612 613 614 /*************************************************************************** 615 * * 616 * Private implementation * 617 * * 618 **************************************************************************/ 619 620 private void initialize() { 1022 : 0; 1023 } 1024 1025 /** 1026 * Computes the size that should be reserved for vertical scrollbar in size hints (min/pref width) 1027 */ 1028 private double computeVsbSizeHint(ScrollPane sp) { 1029 return ((sp.getVbarPolicy() == ScrollBarPolicy.ALWAYS) || 1030 (sp.getVbarPolicy() == ScrollBarPolicy.AS_NEEDED && (sp.getPrefViewportWidth() > 0 1031 || sp.getMinViewportWidth() > 0))) 1032 ? vsb.prefWidth(ScrollBar.USE_COMPUTED_SIZE) 1033 : 0; 1034 } 1035 1036 private void computeScrollNodeSize(double contentWidth, double contentHeight) { 1037 if (scrollNode != null) { 1038 if (scrollNode.isResizable()) { 1039 ScrollPane control = getSkinnable(); 1040 Orientation bias = scrollNode.getContentBias(); 1041 if (bias == null) { 1042 nodeWidth = snapSize(boundedSize(control.isFitToWidth()? contentWidth : scrollNode.prefWidth(-1), 1043 scrollNode.minWidth(-1),scrollNode.maxWidth(-1))); 1044 nodeHeight = snapSize(boundedSize(control.isFitToHeight()? contentHeight : scrollNode.prefHeight(-1), 1045 scrollNode.minHeight(-1), scrollNode.maxHeight(-1))); 1046 1047 } else if (bias == Orientation.HORIZONTAL) { 1048 nodeWidth = snapSize(boundedSize(control.isFitToWidth()? contentWidth : scrollNode.prefWidth(-1), 1049 scrollNode.minWidth(-1),scrollNode.maxWidth(-1))); 1050 nodeHeight = snapSize(boundedSize(control.isFitToHeight()? contentHeight : scrollNode.prefHeight(nodeWidth), 1051 scrollNode.minHeight(nodeWidth),scrollNode.maxHeight(nodeWidth))); 1052 1053 } else { // bias == VERTICAL 1054 nodeHeight = snapSize(boundedSize(control.isFitToHeight()? contentHeight : scrollNode.prefHeight(-1), 1055 scrollNode.minHeight(-1), scrollNode.maxHeight(-1))); 1056 nodeWidth = snapSize(boundedSize(control.isFitToWidth()? contentWidth : scrollNode.prefWidth(nodeHeight), 1057 scrollNode.minWidth(nodeHeight),scrollNode.maxWidth(nodeHeight))); 1058 } 1059 1060 } else { 1061 nodeWidth = snapSize(scrollNode.getLayoutBounds().getWidth()); 1062 nodeHeight = snapSize(scrollNode.getLayoutBounds().getHeight()); 1063 } 1064 nodeSizeInvalid = false; 1065 } 1066 } 1067 1068 private boolean isReverseNodeOrientation() { 1069 return (scrollNode != null && 1070 getSkinnable().getEffectiveNodeOrientation() != 1071 scrollNode.getEffectiveNodeOrientation()); 1072 } 1073 1074 private boolean determineHorizontalSBVisible() { 1075 final ScrollPane sp = getSkinnable(); 1076 1077 if (Properties.IS_TOUCH_SUPPORTED) { 1078 return (tempVisibility && (nodeWidth > contentWidth)); 1079 } 1080 else { 1081 // RT-17395: ScrollBarPolicy might be null. If so, treat it as "AS_NEEDED", which is the default 1082 ScrollBarPolicy hbarPolicy = sp.getHbarPolicy(); 1087 } 1088 } 1089 1090 private boolean determineVerticalSBVisible() { 1091 final ScrollPane sp = getSkinnable(); 1092 1093 if (Properties.IS_TOUCH_SUPPORTED) { 1094 return (tempVisibility && (nodeHeight > contentHeight)); 1095 } 1096 else { 1097 // RT-17395: ScrollBarPolicy might be null. If so, treat it as "AS_NEEDED", which is the default 1098 ScrollBarPolicy vbarPolicy = sp.getVbarPolicy(); 1099 return (ScrollBarPolicy.NEVER == vbarPolicy) ? false : 1100 ((ScrollBarPolicy.ALWAYS == vbarPolicy) ? true : 1101 ((sp.isFitToHeight() && scrollNode != null ? scrollNode.isResizable() : false) ? 1102 (nodeHeight > contentHeight && scrollNode.minHeight(-1) > contentHeight) : (nodeHeight > contentHeight))); 1103 } 1104 } 1105 1106 private void computeScrollBarSize() { 1107 vsbWidth = snapSize(vsb.prefWidth(-1)); 1108 if (vsbWidth == 0) { 1109 // println("*** WARNING ScrollPaneSkin: can't get scroll bar width, using {DEFAULT_SB_BREADTH}"); 1110 if (Properties.IS_TOUCH_SUPPORTED) { 1111 vsbWidth = DEFAULT_EMBEDDED_SB_BREADTH; 1112 } 1113 else { 1114 vsbWidth = DEFAULT_SB_BREADTH; 1115 } 1116 } 1117 hsbHeight = snapSize(hsb.prefHeight(-1)); 1118 if (hsbHeight == 0) { 1119 // println("*** WARNING ScrollPaneSkin: can't get scroll bar height, using {DEFAULT_SB_BREADTH}"); 1120 if (Properties.IS_TOUCH_SUPPORTED) { 1121 hsbHeight = DEFAULT_EMBEDDED_SB_BREADTH; 1122 } 1123 else { 1124 hsbHeight = DEFAULT_SB_BREADTH; 1125 } 1126 } 1127 } 1128 1129 private void updateHorizontalSB() { 1130 double contentRatio = nodeWidth * (hsb.getMax() - hsb.getMin()); 1131 if (contentRatio > 0.0) { 1132 hsb.setVisibleAmount(contentWidth / contentRatio); 1133 hsb.setBlockIncrement(0.9 * hsb.getVisibleAmount()); 1134 hsb.setUnitIncrement(0.1 * hsb.getVisibleAmount()); 1135 } 1136 else { 1137 hsb.setVisibleAmount(0.0); 1161 vsb.setVisibleAmount(0.0); 1162 vsb.setBlockIncrement(0.0); 1163 vsb.setUnitIncrement(0.0); 1164 } 1165 1166 if (vsb.isVisible()) { 1167 updatePosY(); 1168 } else { 1169 if (nodeHeight > contentHeight) { 1170 updatePosY(); 1171 } else { 1172 viewContent.setLayoutY(0); 1173 } 1174 } 1175 } 1176 1177 private double updatePosX() { 1178 final ScrollPane sp = getSkinnable(); 1179 double x = isReverseNodeOrientation() ? (hsb.getMax() - (posX - hsb.getMin())) : posX; 1180 double minX = Math.min((- x / (hsb.getMax() - hsb.getMin()) * (nodeWidth - contentWidth)), 0); 1181 viewContent.setLayoutX(snapPosition(minX)); 1182 if (!sp.hvalueProperty().isBound()) sp.setHvalue(Utils.clamp(sp.getHmin(), posX, sp.getHmax())); 1183 return posX; 1184 } 1185 1186 private double updatePosY() { 1187 final ScrollPane sp = getSkinnable(); 1188 double minY = Math.min((- posY / (vsb.getMax() - vsb.getMin()) * (nodeHeight - contentHeight)), 0); 1189 viewContent.setLayoutY(snapPosition(minY)); 1190 if (!sp.vvalueProperty().isBound()) sp.setVvalue(Utils.clamp(sp.getVmin(), posY, sp.getVmax())); 1191 return posY; 1192 } 1193 1194 private void resetClip() { 1195 clipRect.setWidth(snapSize(contentWidth)); 1196 clipRect.setHeight(snapSize(contentHeight)); 1197 } 1198 1199 private void startSBReleasedAnimation() { 1200 if (sbTouchTimeline == null) { 1201 /* 1202 ** timeline to leave the scrollbars visible for a short 1203 ** while after a scroll/drag 1204 */ 1205 sbTouchTimeline = new Timeline(); 1206 sbTouchKF1 = new KeyFrame(Duration.millis(0), event -> { 1207 tempVisibility = true; 1208 if (touchDetected == true || mouseDown == true) { 1209 sbTouchTimeline.playFromStart(); 1210 } 1211 }); 1212 1213 sbTouchKF2 = new KeyFrame(Duration.millis(1000), event -> { 1214 tempVisibility = false; 1215 getSkinnable().requestLayout(); 1216 }); | 183 } 184 } 185 }; 186 187 188 /* 189 ** The content of the ScrollPane has just changed bounds, check scrollBar positions. 190 */ 191 private final ChangeListener<Bounds> boundsChangeListener = new ChangeListener<Bounds>() { 192 @Override public void changed(ObservableValue<? extends Bounds> observable, Bounds oldBounds, Bounds newBounds) { 193 194 /* 195 ** For a height change then we want to reduce 196 ** viewport vertical jumping as much as possible. 197 ** We set a new vsb value to try to keep the same 198 ** content position at the top of the viewport 199 */ 200 double oldHeight = oldBounds.getHeight(); 201 double newHeight = newBounds.getHeight(); 202 if (oldHeight > 0 && oldHeight != newHeight) { 203 double oldPositionY = (snapPositionY(snappedTopInset() - posY / (vsb.getMax() - vsb.getMin()) * (oldHeight - contentHeight))); 204 double newPositionY = (snapPositionY(snappedTopInset() - posY / (vsb.getMax() - vsb.getMin()) * (newHeight - contentHeight))); 205 206 double newValueY = (oldPositionY/newPositionY)*vsb.getValue(); 207 if (newValueY < 0.0) { 208 vsb.setValue(0.0); 209 } 210 else if (newValueY < 1.0) { 211 vsb.setValue(newValueY); 212 } 213 else if (newValueY > 1.0) { 214 vsb.setValue(1.0); 215 } 216 } 217 218 /* 219 ** For a width change then we want to reduce 220 ** viewport horizontal jumping as much as possible. 221 ** We set a new hsb value to try to keep the same 222 ** content position to the left of the viewport 223 */ 224 double oldWidth = oldBounds.getWidth(); 225 double newWidth = newBounds.getWidth(); 226 if (oldWidth > 0 && oldWidth != newWidth) { 227 double oldPositionX = (snapPositionX(snappedLeftInset() - posX / (hsb.getMax() - hsb.getMin()) * (oldWidth - contentWidth))); 228 double newPositionX = (snapPositionX(snappedLeftInset() - posX / (hsb.getMax() - hsb.getMin()) * (newWidth - contentWidth))); 229 230 double newValueX = (oldPositionX/newPositionX)*hsb.getValue(); 231 if (newValueX < 0.0) { 232 hsb.setValue(0.0); 233 } 234 else if (newValueX < 1.0) { 235 hsb.setValue(newValueX); 236 } 237 else if (newValueX > 1.0) { 238 hsb.setValue(1.0); 239 } 240 } 241 } 242 }; 243 244 245 246 /*************************************************************************** 247 * * 248 * Constructors * 262 // install default input map for the ScrollPane control 263 behavior = new ScrollPaneBehavior(control); 264 // control.setInputMap(behavior.getInputMap()); 265 266 initialize(); 267 268 // Register listeners 269 Consumer<ObservableValue<?>> viewportSizeHintConsumer = e -> { 270 // change affects pref size, so requestLayout on control 271 getSkinnable().requestLayout(); 272 }; 273 registerChangeListener(control.contentProperty(), e -> { 274 if (scrollNode != getSkinnable().getContent()) { 275 if (scrollNode != null) { 276 scrollNode.layoutBoundsProperty().removeListener(nodeListener); 277 scrollNode.layoutBoundsProperty().removeListener(boundsChangeListener); 278 viewContent.getChildren().remove(scrollNode); 279 } 280 scrollNode = getSkinnable().getContent(); 281 if (scrollNode != null) { 282 nodeWidth = snapSizeX(scrollNode.getLayoutBounds().getWidth()); 283 nodeHeight = snapSizeY(scrollNode.getLayoutBounds().getHeight()); 284 viewContent.getChildren().setAll(scrollNode); 285 scrollNode.layoutBoundsProperty().addListener(nodeListener); 286 scrollNode.layoutBoundsProperty().addListener(boundsChangeListener); 287 } 288 } 289 getSkinnable().requestLayout(); 290 }); 291 registerChangeListener(control.fitToWidthProperty(), e -> { 292 getSkinnable().requestLayout(); 293 viewRect.requestLayout(); 294 }); 295 registerChangeListener(control.fitToHeightProperty(), e -> { 296 getSkinnable().requestLayout(); 297 viewRect.requestLayout(); 298 }); 299 registerChangeListener(control.hbarPolicyProperty(), e -> { 300 // change might affect pref size, so requestLayout on control 301 getSkinnable().requestLayout(); 302 }); 303 registerChangeListener(control.vbarPolicyProperty(), e -> { 461 462 /** {@inheritDoc} */ 463 @Override protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) { 464 final ScrollPane sp = getSkinnable(); 465 466 double hsbHeight = computeHsbSizeHint(sp); 467 double minHeight = hsbHeight + snappedTopInset() + snappedBottomInset(); 468 469 if (sp.getMinViewportHeight() > 0) { 470 return (sp.getMinViewportHeight() + minHeight); 471 } else { 472 double h = corner.minHeight(-1); 473 return (h > 0) ? (3 * h) : (DEFAULT_MIN_SIZE); 474 } 475 } 476 477 @Override protected void layoutChildren(final double x, final double y, 478 final double w, final double h) { 479 final ScrollPane control = getSkinnable(); 480 final Insets padding = control.getPadding(); 481 final double rightPadding = snapSizeX(padding.getRight()); 482 final double leftPadding = snapSizeX(padding.getLeft()); 483 final double topPadding = snapSizeY(padding.getTop()); 484 final double bottomPadding = snapSizeY(padding.getBottom()); 485 486 vsb.setMin(control.getVmin()); 487 vsb.setMax(control.getVmax()); 488 489 //should only do this on css setup 490 hsb.setMin(control.getHmin()); 491 hsb.setMax(control.getHmax()); 492 493 contentWidth = w; 494 contentHeight = h; 495 496 /* 497 ** we want the scrollbars to go right to the border 498 */ 499 double hsbWidth = 0; 500 double vsbHeight = 0; 501 502 computeScrollNodeSize(contentWidth, contentHeight); 503 computeScrollBarSize(); 504 569 570 hsb.setVisible(hsbvis); 571 if (hsbvis) { 572 /* 573 ** round up position of ScrollBar, round down it's size. 574 ** 575 ** Positioning the ScrollBar 576 ** The Padding should go between the content and the edge, 577 ** otherwise changes in padding move the ScrollBar, and could 578 ** in extreme cases size the ScrollBar to become unusable. 579 ** The -1, +1 plus one bit : 580 ** If padding in => 1 then we allow one pixel to appear as the 581 ** outside border of the Scrollbar, and the rest on the inside. 582 ** If padding is < 1 then we just stick to the edge. 583 */ 584 hsb.resizeRelocate(cx, snappedTopInset() + h - hsbHeight + (bottomPadding < 1 ? 0 : bottomPadding - 1), 585 hsbWidth, hsbHeight); 586 } 587 updateHorizontalSB(); 588 589 viewRect.resizeRelocate(snappedLeftInset(), snappedTopInset(), snapSizeX(contentWidth), snapSizeY(contentHeight)); 590 resetClip(); 591 592 if (vsbvis && hsbvis) { 593 corner.setVisible(true); 594 double cornerWidth = vsbWidth; 595 double cornerHeight = hsbHeight; 596 corner.resizeRelocate(snapPositionX(vsb.getLayoutX()), snapPositionY(hsb.getLayoutY()), snapSizeX(cornerWidth), snapSizeY(cornerHeight)); 597 } else { 598 corner.setVisible(false); 599 } 600 control.setViewportBounds(new BoundingBox(snapPositionX(viewContent.getLayoutX()), snapPositionY(viewContent.getLayoutY()), snapSizeX(contentWidth), snapSizeY(contentHeight))); 601 } 602 603 /** {@inheritDoc} */ 604 @Override protected Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) { 605 switch (attribute) { 606 case VERTICAL_SCROLLBAR: return vsb; 607 case HORIZONTAL_SCROLLBAR: return hsb; 608 default: return super.queryAccessibleAttribute(attribute, parameters); 609 } 610 } 611 612 613 614 /*************************************************************************** 615 * * 616 * Private implementation * 617 * * 618 **************************************************************************/ 619 620 private void initialize() { 1022 : 0; 1023 } 1024 1025 /** 1026 * Computes the size that should be reserved for vertical scrollbar in size hints (min/pref width) 1027 */ 1028 private double computeVsbSizeHint(ScrollPane sp) { 1029 return ((sp.getVbarPolicy() == ScrollBarPolicy.ALWAYS) || 1030 (sp.getVbarPolicy() == ScrollBarPolicy.AS_NEEDED && (sp.getPrefViewportWidth() > 0 1031 || sp.getMinViewportWidth() > 0))) 1032 ? vsb.prefWidth(ScrollBar.USE_COMPUTED_SIZE) 1033 : 0; 1034 } 1035 1036 private void computeScrollNodeSize(double contentWidth, double contentHeight) { 1037 if (scrollNode != null) { 1038 if (scrollNode.isResizable()) { 1039 ScrollPane control = getSkinnable(); 1040 Orientation bias = scrollNode.getContentBias(); 1041 if (bias == null) { 1042 nodeWidth = snapSizeX(boundedSize(control.isFitToWidth()? contentWidth : scrollNode.prefWidth(-1), 1043 scrollNode.minWidth(-1),scrollNode.maxWidth(-1))); 1044 nodeHeight = snapSizeY(boundedSize(control.isFitToHeight()? contentHeight : scrollNode.prefHeight(-1), 1045 scrollNode.minHeight(-1), scrollNode.maxHeight(-1))); 1046 1047 } else if (bias == Orientation.HORIZONTAL) { 1048 nodeWidth = snapSizeX(boundedSize(control.isFitToWidth()? contentWidth : scrollNode.prefWidth(-1), 1049 scrollNode.minWidth(-1),scrollNode.maxWidth(-1))); 1050 nodeHeight = snapSizeY(boundedSize(control.isFitToHeight()? contentHeight : scrollNode.prefHeight(nodeWidth), 1051 scrollNode.minHeight(nodeWidth),scrollNode.maxHeight(nodeWidth))); 1052 1053 } else { // bias == VERTICAL 1054 nodeHeight = snapSizeY(boundedSize(control.isFitToHeight()? contentHeight : scrollNode.prefHeight(-1), 1055 scrollNode.minHeight(-1), scrollNode.maxHeight(-1))); 1056 nodeWidth = snapSizeX(boundedSize(control.isFitToWidth()? contentWidth : scrollNode.prefWidth(nodeHeight), 1057 scrollNode.minWidth(nodeHeight),scrollNode.maxWidth(nodeHeight))); 1058 } 1059 1060 } else { 1061 nodeWidth = snapSizeX(scrollNode.getLayoutBounds().getWidth()); 1062 nodeHeight = snapSizeY(scrollNode.getLayoutBounds().getHeight()); 1063 } 1064 nodeSizeInvalid = false; 1065 } 1066 } 1067 1068 private boolean isReverseNodeOrientation() { 1069 return (scrollNode != null && 1070 getSkinnable().getEffectiveNodeOrientation() != 1071 scrollNode.getEffectiveNodeOrientation()); 1072 } 1073 1074 private boolean determineHorizontalSBVisible() { 1075 final ScrollPane sp = getSkinnable(); 1076 1077 if (Properties.IS_TOUCH_SUPPORTED) { 1078 return (tempVisibility && (nodeWidth > contentWidth)); 1079 } 1080 else { 1081 // RT-17395: ScrollBarPolicy might be null. If so, treat it as "AS_NEEDED", which is the default 1082 ScrollBarPolicy hbarPolicy = sp.getHbarPolicy(); 1087 } 1088 } 1089 1090 private boolean determineVerticalSBVisible() { 1091 final ScrollPane sp = getSkinnable(); 1092 1093 if (Properties.IS_TOUCH_SUPPORTED) { 1094 return (tempVisibility && (nodeHeight > contentHeight)); 1095 } 1096 else { 1097 // RT-17395: ScrollBarPolicy might be null. If so, treat it as "AS_NEEDED", which is the default 1098 ScrollBarPolicy vbarPolicy = sp.getVbarPolicy(); 1099 return (ScrollBarPolicy.NEVER == vbarPolicy) ? false : 1100 ((ScrollBarPolicy.ALWAYS == vbarPolicy) ? true : 1101 ((sp.isFitToHeight() && scrollNode != null ? scrollNode.isResizable() : false) ? 1102 (nodeHeight > contentHeight && scrollNode.minHeight(-1) > contentHeight) : (nodeHeight > contentHeight))); 1103 } 1104 } 1105 1106 private void computeScrollBarSize() { 1107 vsbWidth = snapSizeX(vsb.prefWidth(-1)); 1108 if (vsbWidth == 0) { 1109 // println("*** WARNING ScrollPaneSkin: can't get scroll bar width, using {DEFAULT_SB_BREADTH}"); 1110 if (Properties.IS_TOUCH_SUPPORTED) { 1111 vsbWidth = DEFAULT_EMBEDDED_SB_BREADTH; 1112 } 1113 else { 1114 vsbWidth = DEFAULT_SB_BREADTH; 1115 } 1116 } 1117 hsbHeight = snapSizeY(hsb.prefHeight(-1)); 1118 if (hsbHeight == 0) { 1119 // println("*** WARNING ScrollPaneSkin: can't get scroll bar height, using {DEFAULT_SB_BREADTH}"); 1120 if (Properties.IS_TOUCH_SUPPORTED) { 1121 hsbHeight = DEFAULT_EMBEDDED_SB_BREADTH; 1122 } 1123 else { 1124 hsbHeight = DEFAULT_SB_BREADTH; 1125 } 1126 } 1127 } 1128 1129 private void updateHorizontalSB() { 1130 double contentRatio = nodeWidth * (hsb.getMax() - hsb.getMin()); 1131 if (contentRatio > 0.0) { 1132 hsb.setVisibleAmount(contentWidth / contentRatio); 1133 hsb.setBlockIncrement(0.9 * hsb.getVisibleAmount()); 1134 hsb.setUnitIncrement(0.1 * hsb.getVisibleAmount()); 1135 } 1136 else { 1137 hsb.setVisibleAmount(0.0); 1161 vsb.setVisibleAmount(0.0); 1162 vsb.setBlockIncrement(0.0); 1163 vsb.setUnitIncrement(0.0); 1164 } 1165 1166 if (vsb.isVisible()) { 1167 updatePosY(); 1168 } else { 1169 if (nodeHeight > contentHeight) { 1170 updatePosY(); 1171 } else { 1172 viewContent.setLayoutY(0); 1173 } 1174 } 1175 } 1176 1177 private double updatePosX() { 1178 final ScrollPane sp = getSkinnable(); 1179 double x = isReverseNodeOrientation() ? (hsb.getMax() - (posX - hsb.getMin())) : posX; 1180 double minX = Math.min((- x / (hsb.getMax() - hsb.getMin()) * (nodeWidth - contentWidth)), 0); 1181 viewContent.setLayoutX(snapPositionX(minX)); 1182 if (!sp.hvalueProperty().isBound()) sp.setHvalue(Utils.clamp(sp.getHmin(), posX, sp.getHmax())); 1183 return posX; 1184 } 1185 1186 private double updatePosY() { 1187 final ScrollPane sp = getSkinnable(); 1188 double minY = Math.min((- posY / (vsb.getMax() - vsb.getMin()) * (nodeHeight - contentHeight)), 0); 1189 viewContent.setLayoutY(snapPositionY(minY)); 1190 if (!sp.vvalueProperty().isBound()) sp.setVvalue(Utils.clamp(sp.getVmin(), posY, sp.getVmax())); 1191 return posY; 1192 } 1193 1194 private void resetClip() { 1195 clipRect.setWidth(snapSizeX(contentWidth)); 1196 clipRect.setHeight(snapSizeY(contentHeight)); 1197 } 1198 1199 private void startSBReleasedAnimation() { 1200 if (sbTouchTimeline == null) { 1201 /* 1202 ** timeline to leave the scrollbars visible for a short 1203 ** while after a scroll/drag 1204 */ 1205 sbTouchTimeline = new Timeline(); 1206 sbTouchKF1 = new KeyFrame(Duration.millis(0), event -> { 1207 tempVisibility = true; 1208 if (touchDetected == true || mouseDown == true) { 1209 sbTouchTimeline.playFromStart(); 1210 } 1211 }); 1212 1213 sbTouchKF2 = new KeyFrame(Duration.millis(1000), event -> { 1214 tempVisibility = false; 1215 getSkinnable().requestLayout(); 1216 }); |