1 /*
   2  * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.scene.control;
  27 
  28 import java.util.ArrayList;
  29 import java.util.Collections;
  30 import java.util.List;
  31 
  32 import javafx.beans.DefaultProperty;
  33 import javafx.beans.property.BooleanProperty;
  34 import javafx.beans.property.DoubleProperty;
  35 import javafx.beans.property.ObjectProperty;
  36 import javafx.beans.property.SimpleDoubleProperty;
  37 import javafx.beans.property.SimpleObjectProperty;
  38 import javafx.beans.value.WritableValue;
  39 import javafx.css.CssMetaData;
  40 import javafx.css.PseudoClass;
  41 import javafx.css.StyleableBooleanProperty;
  42 import javafx.css.StyleableObjectProperty;
  43 import javafx.css.StyleableProperty;
  44 import javafx.geometry.BoundingBox;
  45 import javafx.geometry.Bounds;
  46 import javafx.scene.AccessibleAttribute;
  47 import javafx.scene.AccessibleRole;
  48 import javafx.scene.Node;
  49 
  50 import com.sun.javafx.css.converters.BooleanConverter;
  51 import com.sun.javafx.css.converters.EnumConverter;
  52 import com.sun.javafx.scene.control.skin.ScrollPaneSkin;
  53 
  54 import javafx.css.Styleable;
  55 
  56 /**
  57  * A Control that provides a scrolled, clipped viewport of its contents. It
  58  * allows the user to scroll the content around either directly (panning) or
  59  * by using scroll bars. The ScrollPane allows specification of the scroll
  60  * bar policy, which determines when scroll bars are displayed: always, never,
  61  * or only when they are needed. The scroll bar policy can be specified
  62  * independently for the horizontal and vertical scroll bars.
  63  * <p>
  64  * The ScrollPane allows the application to set the current, minimum, and
  65  * maximum values for positioning the contents in the horizontal and
  66  * vertical directions. These values are mapped proportionally onto the
  67  * {@link javafx.scene.Node#layoutBoundsProperty layoutBounds} of the contained node.
  68  * <p>
  69  * ScrollPane layout calculations are based on the layoutBounds rather than
  70  * the boundsInParent (visual bounds) of the scroll node.
  71  * If an application wants the scrolling to be based on the visual bounds
  72  * of the node (for scaled content etc.), they need to wrap the scroll
  73  * node in a Group.
  74  * <p>
  75  * ScrollPane sets focusTraversable to false.
  76  * </p>
  77  *
  78  * <p>
  79  * This example creates a ScrollPane, which contains a Rectangle :
  80  * <pre><code>
  81  * import javafx.scene.control.ScrollPane;
  82  * import javafx.scene.shape.Rectangle;
  83  * 
  84  * Rectangle rect = new Rectangle(200, 200, Color.RED);
  85  * ScrollPane s1 = new ScrollPane();
  86  * s1.setPrefSize(120, 120);
  87  * s1.setContent(rect);
  88  * </code></pre>
  89  *
  90  * Implementation of ScrollPane According to JavaFX UI Control API Specification
  91  * @since JavaFX 2.0
  92  */
  93 @DefaultProperty("content")
  94 public class ScrollPane extends Control {
  95 
  96     /***************************************************************************
  97      *                                                                         *
  98      * Constructors                                                            *
  99      *                                                                         *
 100      **************************************************************************/
 101 
 102     /**
 103      * Creates a new ScrollPane.
 104      */
 105     public ScrollPane() {
 106         getStyleClass().setAll(DEFAULT_STYLE_CLASS);
 107         setAccessibleRole(AccessibleRole.SCROLL_PANE);
 108         // focusTraversable is styleable through css. Calling setFocusTraversable
 109         // makes it look to css like the user set the value and css will not 
 110         // override. Initializing focusTraversable by calling applyStyle with
 111         // null StyleOrigin ensures that css will be able to override the value.
 112         ((StyleableProperty<Boolean>)(WritableValue<Boolean>)focusTraversableProperty()).applyStyle(null, Boolean.FALSE); 
 113     }
 114 
 115     /**
 116      * Creates a new ScrollPane.
 117      * @param content the initial content for the ScrollPane
 118      * @since JavaFX 8.0
 119      */
 120     public ScrollPane(Node content) {
 121         this();
 122         setContent(content);
 123     }
 124 
 125     /***************************************************************************
 126      *                                                                         *
 127      * Properties                                                              *
 128      *                                                                         *
 129      **************************************************************************/
 130     /**
 131      * Specifies the policy for showing the horizontal scroll bar.
 132      */
 133     private ObjectProperty<ScrollBarPolicy> hbarPolicy;
 134     public final void setHbarPolicy(ScrollBarPolicy value) {
 135         hbarPolicyProperty().set(value);
 136     }
 137 
 138     public final ScrollBarPolicy getHbarPolicy() {
 139         return hbarPolicy == null ? ScrollBarPolicy.AS_NEEDED : hbarPolicy.get();
 140     }
 141 
 142     public final ObjectProperty<ScrollBarPolicy> hbarPolicyProperty() {
 143         if (hbarPolicy == null) {
 144             hbarPolicy = new StyleableObjectProperty<ScrollBarPolicy>(ScrollBarPolicy.AS_NEEDED) {
 145 
 146                 @Override
 147                 public CssMetaData<ScrollPane,ScrollBarPolicy> getCssMetaData() {
 148                     return StyleableProperties.HBAR_POLICY;
 149                 }
 150 
 151                 @Override
 152                 public Object getBean() {
 153                     return ScrollPane.this;
 154                 }
 155 
 156                 @Override
 157                 public String getName() {
 158                     return "hbarPolicy";
 159                 }
 160             };
 161         }
 162         return hbarPolicy;
 163     }
 164     /**
 165      * Specifies the policy for showing the vertical scroll bar.
 166      */
 167     private ObjectProperty<ScrollBarPolicy> vbarPolicy;
 168     public final void setVbarPolicy(ScrollBarPolicy value) {
 169         vbarPolicyProperty().set(value);
 170     }
 171 
 172     public final ScrollBarPolicy getVbarPolicy() {
 173         return vbarPolicy == null ? ScrollBarPolicy.AS_NEEDED : vbarPolicy.get();
 174     }
 175 
 176     public final ObjectProperty<ScrollBarPolicy> vbarPolicyProperty() {
 177         if (vbarPolicy == null) {
 178             vbarPolicy = new StyleableObjectProperty<ScrollBarPolicy>(ScrollBarPolicy.AS_NEEDED) {
 179 
 180                 @Override
 181                 public CssMetaData<ScrollPane,ScrollBarPolicy> getCssMetaData() {
 182                     return StyleableProperties.VBAR_POLICY;
 183                 }
 184 
 185                 @Override
 186                 public Object getBean() {
 187                     return ScrollPane.this;
 188                 }
 189 
 190                 @Override
 191                 public String getName() {
 192                     return "vbarPolicy";
 193                 }
 194             };
 195         }
 196         return vbarPolicy;
 197     }
 198     /**
 199      * The node used as the content of this ScrollPane.
 200      */
 201     private ObjectProperty<Node> content;
 202 
 203     public final void setContent(Node value) {
 204         contentProperty().set(value);
 205     }
 206 
 207     public final Node getContent() {
 208         return content == null ? null : content.get();
 209     }
 210 
 211     public final ObjectProperty<Node> contentProperty() {
 212         if (content == null) {
 213             content = new SimpleObjectProperty<Node>(this, "content");
 214         }
 215         return content;
 216     }
 217     /**
 218      * The current horizontal scroll position of the ScrollPane. This value
 219      * may be set by the application to scroll the view programatically.
 220      * The ScrollPane will update this value whenever the viewport is
 221      * scrolled or panned by the user. This value must always be within
 222      * the range of {@link #hminProperty hmin} to {@link #hmaxProperty hmax}. When {@link #hvalueProperty hvalue}
 223      * equals {@link #hminProperty hmin}, the contained node is positioned so that
 224      * its layoutBounds {@link javafx.geometry.Bounds#getMinX minX} is visible. When {@link #hvalueProperty hvalue}
 225      * equals {@link #hmaxProperty hmax}, the contained node is positioned so that its
 226      * layoutBounds {@link javafx.geometry.Bounds#getMaxX maxX} is visible. When {@link #hvalueProperty hvalue} is between
 227      * {@link #hminProperty hmin} and {@link #hmaxProperty hmax}, the contained node is positioned
 228      * proportionally between layoutBounds {@link javafx.geometry.Bounds#getMinX minX} and
 229      * layoutBounds {@link javafx.geometry.Bounds#getMaxX maxX}.
 230      */
 231     private DoubleProperty hvalue;
 232 
 233     public final void setHvalue(double value) {
 234         hvalueProperty().set(value);
 235     }
 236 
 237     public final double getHvalue() {
 238         return hvalue == null ? 0.0 : hvalue.get();
 239     }
 240 
 241     public final DoubleProperty hvalueProperty() {
 242         if (hvalue == null) {
 243             hvalue = new SimpleDoubleProperty(this, "hvalue");
 244         }
 245         return hvalue;
 246     }
 247     /**
 248      * The current vertical scroll position of the ScrollPane. This value
 249      * may be set by the application to scroll the view programatically.
 250      * The ScrollPane will update this value whenever the viewport is
 251      * scrolled or panned by the user. This value must always be within
 252      * the range of {@link #vminProperty vmin} to {@link #vmaxProperty vmax}. When {@link #vvalueProperty vvalue}
 253      * equals {@link #vminProperty vmin}, the contained node is positioned so that
 254      * its layoutBounds {@link javafx.geometry.Bounds#getMinY minY} is visible. When {@link #vvalueProperty vvalue}
 255      * equals {@link #vmaxProperty vmax}, the contained node is positioned so that its
 256      * layoutBounds {@link javafx.geometry.Bounds#getMaxY maxY} is visible. When {@link #vvalueProperty vvalue} is between
 257      * {@link #vminProperty vmin} and {@link #vmaxProperty vmax}, the contained node is positioned
 258      * proportionally between layoutBounds {@link javafx.geometry.Bounds#getMinY minY} and
 259      * layoutBounds {@link javafx.geometry.Bounds#getMaxY maxY}.
 260      */
 261     private DoubleProperty vvalue;
 262 
 263     public final void setVvalue(double value) {
 264         vvalueProperty().set(value);
 265     }
 266 
 267     public final double getVvalue() {
 268         return vvalue == null ? 0.0 : vvalue.get();
 269     }
 270 
 271     public final DoubleProperty vvalueProperty() {
 272         if (vvalue == null) {
 273             vvalue = new SimpleDoubleProperty(this, "vvalue");
 274         }
 275         return vvalue;
 276     }
 277     /**
 278      * The minimum allowable {@link #hvalueProperty hvalue} for this ScrollPane.
 279      * Default value is 0.
 280      */
 281     private DoubleProperty hmin;
 282 
 283     public final void setHmin(double value) {
 284         hminProperty().set(value);
 285     }
 286 
 287     public final double getHmin() {
 288         return hmin == null ? 0.0F : hmin.get();
 289     }
 290 
 291     public final DoubleProperty hminProperty() {
 292         if (hmin == null) {
 293             hmin = new SimpleDoubleProperty(this, "hmin", 0.0);
 294         }
 295         return hmin;
 296     }
 297     /**
 298      * The minimum allowable {@link #hvalueProperty vvalue} for this ScrollPane.
 299      * Default value is 0.
 300      */
 301     private DoubleProperty vmin;
 302 
 303     public final void setVmin(double value) {
 304         vminProperty().set(value);
 305     }
 306 
 307     public final double getVmin() {
 308         return vmin == null ? 0.0F : vmin.get();
 309     }
 310 
 311     public final DoubleProperty vminProperty() {
 312         if (vmin == null) {
 313             vmin = new SimpleDoubleProperty(this, "vmin", 0.0);
 314         }
 315         return vmin;
 316     }
 317     /**
 318      * The maximum allowable {@link #hvalueProperty hvalue} for this ScrollPane.
 319      * Default value is 1.
 320      */
 321     private DoubleProperty hmax;
 322 
 323     public final void setHmax(double value) {
 324         hmaxProperty().set(value);
 325     }
 326 
 327     public final double getHmax() {
 328         return hmax == null ? 1.0F : hmax.get();
 329     }
 330 
 331     public final DoubleProperty hmaxProperty() {
 332         if (hmax == null) {
 333             hmax = new SimpleDoubleProperty(this, "hmax", 1.0);
 334         }
 335         return hmax;
 336     }
 337     /**
 338      * The maximum allowable {@link #hvalueProperty vvalue} for this ScrollPane.
 339      * Default value is 1.
 340      */
 341     private DoubleProperty vmax;
 342 
 343     public final void setVmax(double value) {
 344         vmaxProperty().set(value);
 345     }
 346 
 347     public final double getVmax() {
 348         return vmax == null ? 1.0F : vmax.get();
 349     }
 350 
 351     public final DoubleProperty vmaxProperty() {
 352         if (vmax == null) {
 353             vmax = new SimpleDoubleProperty(this, "vmax", 1.0);
 354         }
 355         return vmax;
 356     }
 357     /**
 358      * If true and if the contained node is a Resizable, then the node will be
 359      * kept resized to match the width of the ScrollPane's viewport. If the
 360      * contained node is not a Resizable, this value is ignored.
 361      */
 362     private BooleanProperty fitToWidth;
 363     public final void setFitToWidth(boolean value) {
 364         fitToWidthProperty().set(value);
 365     }
 366     public final boolean isFitToWidth() {
 367         return fitToWidth == null ? false : fitToWidth.get();
 368     }
 369     public final BooleanProperty fitToWidthProperty() {
 370         if (fitToWidth == null) {
 371             fitToWidth = new StyleableBooleanProperty(false) {
 372                 @Override public void invalidated() {
 373                     pseudoClassStateChanged(FIT_TO_WIDTH_PSEUDOCLASS_STATE, get());
 374                 }
 375                 
 376                 @Override
 377                 public CssMetaData<ScrollPane,Boolean> getCssMetaData() {
 378                     return StyleableProperties.FIT_TO_WIDTH;
 379                 }
 380 
 381                 @Override
 382                 public Object getBean() {
 383                     return ScrollPane.this;
 384                 }
 385 
 386                 @Override
 387                 public String getName() {
 388                     return "fitToWidth";
 389                 }
 390             };
 391         }
 392         return fitToWidth;
 393     }
 394     /**
 395      * If true and if the contained node is a Resizable, then the node will be
 396      * kept resized to match the height of the ScrollPane's viewport. If the
 397      * contained node is not a Resizable, this value is ignored.
 398      */
 399     private BooleanProperty fitToHeight;
 400     public final void setFitToHeight(boolean value) {
 401         fitToHeightProperty().set(value);
 402     }
 403     public final boolean isFitToHeight() {
 404         return fitToHeight == null ? false : fitToHeight.get();
 405     }
 406     public final BooleanProperty fitToHeightProperty() {
 407         if (fitToHeight == null) {
 408             fitToHeight = new StyleableBooleanProperty(false) {
 409                 @Override public void invalidated() {
 410                     pseudoClassStateChanged(FIT_TO_HEIGHT_PSEUDOCLASS_STATE, get());
 411                 }
 412 
 413                 @Override
 414                 public CssMetaData<ScrollPane,Boolean> getCssMetaData() {
 415                     return StyleableProperties.FIT_TO_HEIGHT;
 416                 }
 417 
 418                 @Override
 419                 public Object getBean() {
 420                     return ScrollPane.this;
 421                 }
 422 
 423                 @Override
 424                 public String getName() {
 425                     return "fitToHeight";
 426                 }
 427             };
 428         }
 429         return fitToHeight;
 430     }
 431     /**
 432      * Specifies whether the user should be able to pan the viewport by using
 433      * the mouse. If mouse events reach the ScrollPane (that is, if mouse
 434      * events are not blocked by the contained node or one of its children)
 435      * then {@link #pannableProperty pannable} is consulted to determine if the events should be
 436      * used for panning.
 437      */
 438     private BooleanProperty pannable;
 439     public final void setPannable(boolean value) {
 440         pannableProperty().set(value);
 441     }
 442     public final boolean isPannable() {
 443         return pannable == null ? false : pannable.get();
 444     }
 445     public final BooleanProperty pannableProperty() {
 446         if (pannable == null) {
 447             pannable = new StyleableBooleanProperty(false) {
 448                 @Override public void invalidated() {
 449                     pseudoClassStateChanged(PANNABLE_PSEUDOCLASS_STATE, get());
 450                 }
 451 
 452                 @Override
 453                 public CssMetaData<ScrollPane,Boolean> getCssMetaData() {
 454                     return StyleableProperties.PANNABLE;
 455                 }
 456                 
 457                 @Override
 458                 public Object getBean() {
 459                     return ScrollPane.this;
 460                 }
 461 
 462                 @Override
 463                 public String getName() {
 464                     return "pannable";
 465                 }
 466             };
 467         }
 468         return pannable;
 469     }
 470 
 471 
 472     /**
 473      * Specify the perferred width of the ScrollPane Viewport.
 474      * This is the width that will be available to the content node.
 475      * The overall width of the ScrollPane is the ViewportWidth + padding
 476      */
 477     private DoubleProperty prefViewportWidth;
 478 
 479     public final void setPrefViewportWidth(double value) {
 480         prefViewportWidthProperty().set(value);
 481     }
 482 
 483     public final double getPrefViewportWidth() {
 484         return prefViewportWidth == null ? 0.0F : prefViewportWidth.get();
 485     }
 486 
 487     public final DoubleProperty prefViewportWidthProperty() {
 488         if (prefViewportWidth == null) {
 489             prefViewportWidth = new SimpleDoubleProperty(this, "prefViewportWidth");
 490         }
 491         return prefViewportWidth;
 492     }
 493 
 494     /**
 495      * Specify the preferred height of the ScrollPane Viewport.
 496      * This is the height that will be available to the content node.
 497      * The overall height of the ScrollPane is the ViewportHeight + padding
 498      */
 499     private DoubleProperty prefViewportHeight;
 500 
 501     public final void setPrefViewportHeight(double value) {
 502         prefViewportHeightProperty().set(value);
 503     }
 504 
 505     public final double getPrefViewportHeight() {
 506         return prefViewportHeight == null ? 0.0F : prefViewportHeight.get();
 507     }
 508 
 509     public final DoubleProperty prefViewportHeightProperty() {
 510         if (prefViewportHeight == null) {
 511             prefViewportHeight = new SimpleDoubleProperty(this, "prefViewportHeight");
 512         }
 513         return prefViewportHeight;
 514     }
 515 
 516 
 517     /**
 518      * Specify the minimum width of the ScrollPane Viewport.
 519      * This is the width that will be available to the content node.
 520      *
 521      * @since JavaFX 8u40
 522      * @see #prefViewportWidthProperty()
 523      */
 524     private DoubleProperty minViewportWidth;
 525 
 526     public final void setMinViewportWidth(double value) {
 527         minViewportWidthProperty().set(value);
 528     }
 529 
 530     public final double getMinViewportWidth() {
 531         return minViewportWidth == null ? 0.0F : minViewportWidth.get();
 532     }
 533 
 534     public final DoubleProperty minViewportWidthProperty() {
 535         if (minViewportWidth == null) {
 536             minViewportWidth = new SimpleDoubleProperty(this, "minViewportWidth");
 537         }
 538         return minViewportWidth;
 539     }
 540 
 541     /**
 542      * Specify the minimum height of the ScrollPane Viewport.
 543      * This is the height that will be available to the content node.
 544      *
 545      * @since JavaFX 8u40
 546      * @see #prefViewportHeightProperty()
 547      */
 548     private DoubleProperty minViewportHeight;
 549 
 550     public final void setMinViewportHeight(double value) {
 551         minViewportHeightProperty().set(value);
 552     }
 553 
 554     public final double getMinViewportHeight() {
 555         return minViewportHeight == null ? 0.0F : minViewportHeight.get();
 556     }
 557 
 558     public final DoubleProperty minViewportHeightProperty() {
 559         if (minViewportHeight == null) {
 560             minViewportHeight = new SimpleDoubleProperty(this, "minViewportHeight");
 561         }
 562         return minViewportHeight;
 563     }
 564 
 565     /**
 566      * The actual Bounds of the ScrollPane Viewport.
 567      * This is the Bounds of the content node.
 568      */
 569     private ObjectProperty<Bounds> viewportBounds;
 570 
 571     public final void setViewportBounds(Bounds value) {
 572         viewportBoundsProperty().set(value);
 573     }
 574 
 575     public final Bounds getViewportBounds() {
 576         return viewportBounds == null ? new BoundingBox(0,0,0,0) : viewportBounds.get();
 577     }
 578 
 579     public final ObjectProperty<Bounds> viewportBoundsProperty() {
 580         if (viewportBounds == null) {
 581             viewportBounds = new SimpleObjectProperty<Bounds>(this, "viewportBounds", new BoundingBox(0,0,0,0));
 582         }
 583         return viewportBounds;
 584     }
 585 
 586 
 587     /***************************************************************************
 588      *                                                                         *
 589      * Methods                                                                 *
 590      *                                                                         *
 591      **************************************************************************/
 592 
 593     /*
 594      * TODO The unit increment and block increment variables have been
 595      * removed from the public API. These are intended to be mapped to
 596      * the corresponding variables of the scrollbars. However, the problem
 597      * is that they are specified in terms of the logical corrdinate space
 598      * of the ScrollPane (that is, [hmin..hmax] by [vmin..vmax]. This is
 599      * incorrect. Scrolling is a user action and should properly be based
 600      * on how much of the content is visible, not on some abstract
 601      * coordinate space. At some later date we may add a finer-grained
 602      * API to allow applications to control this. Meanwhile, the skin should
 603      * set unit and block increments for the scroll bars to do something
 604      * reasonable based on the viewport size, e.g. the block increment
 605      * should scroll 90% of the pixel size of the viewport, and the unit
 606      * increment should scroll 10% of the pixel size of the viewport.
 607      */
 608 
 609     /**
 610      * Defines the horizontal unit increment amount. Typically this is used when clicking on the
 611      * increment or decrement arrow buttons of the horizontal scroll bar.
 612      */
 613     // public var hunitIncrement:Number = 20.0;
 614 
 615     /**
 616      * Defines the vertical unit increment amount. Typically this is used when clicking on the
 617      * increment or decrement arrow buttons of the vertical scroll bar.
 618      */
 619     // public var vunitIncrement:Number = 20.0;
 620 
 621     /**
 622      * Defines the horizontal block increment amount. Typically this is used when clicking on the
 623      * track of the scroll bar.
 624      */
 625     // public var hblockIncrement:Number = -1;
 626 
 627     /**
 628      * Defines the vertical block increment amount. Typically this is used when clicking on the
 629      * track of the scroll bar.
 630      */
 631     // public var vblockIncrement:Number = -1;
 632 
 633     /** {@inheritDoc} */
 634     @Override protected Skin<?> createDefaultSkin() {
 635         return new ScrollPaneSkin(this);
 636     }
 637 
 638     /***************************************************************************
 639      *                                                                         *
 640      *                         Stylesheet Handling                             *
 641      *                                                                         *
 642      **************************************************************************/
 643 
 644     /**
 645      * Initialize the style class to 'scroll-view'.
 646      *
 647      * This is the selector class from which CSS can be used to style
 648      * this control.
 649      */
 650     private static final String DEFAULT_STYLE_CLASS = "scroll-pane";
 651 
 652     /**
 653      * @treatAsPrivate
 654      */
 655     private static class StyleableProperties {
 656         private static final CssMetaData<ScrollPane,ScrollBarPolicy> HBAR_POLICY =
 657             new CssMetaData<ScrollPane,ScrollBarPolicy>("-fx-hbar-policy",
 658                  new EnumConverter<ScrollBarPolicy>(ScrollBarPolicy.class),
 659                         ScrollBarPolicy.AS_NEEDED){
 660 
 661             @Override
 662             public boolean isSettable(ScrollPane n) {
 663                 return n.hbarPolicy == null || !n.hbarPolicy.isBound();
 664             }
 665 
 666             @Override
 667             public StyleableProperty<ScrollBarPolicy> getStyleableProperty(ScrollPane n) {
 668                 return (StyleableProperty<ScrollBarPolicy>)(WritableValue<ScrollBarPolicy>)n.hbarPolicyProperty();
 669             }
 670         };
 671                 
 672         private static final CssMetaData<ScrollPane,ScrollBarPolicy> VBAR_POLICY =
 673             new CssMetaData<ScrollPane,ScrollBarPolicy>("-fx-vbar-policy",
 674                 new EnumConverter<ScrollBarPolicy>(ScrollBarPolicy.class),
 675                         ScrollBarPolicy.AS_NEEDED){
 676 
 677             @Override
 678             public boolean isSettable(ScrollPane n) {
 679                 return n.vbarPolicy == null || !n.vbarPolicy.isBound();
 680             }
 681 
 682             @Override
 683             public StyleableProperty<ScrollBarPolicy> getStyleableProperty(ScrollPane n) {
 684                 return (StyleableProperty<ScrollBarPolicy>)(WritableValue<ScrollBarPolicy>)n.vbarPolicyProperty();
 685             }
 686         };
 687                 
 688         private static final CssMetaData<ScrollPane,Boolean> FIT_TO_WIDTH =
 689             new CssMetaData<ScrollPane, Boolean>("-fx-fit-to-width",
 690                 BooleanConverter.getInstance(), Boolean.FALSE){
 691 
 692             @Override
 693             public boolean isSettable(ScrollPane n) {
 694                 return n.fitToWidth == null || !n.fitToWidth.isBound();
 695             }
 696 
 697             @Override
 698             public StyleableProperty<Boolean> getStyleableProperty(ScrollPane n) {
 699                 return (StyleableProperty<Boolean>)(WritableValue<Boolean>)n.fitToWidthProperty();
 700             }
 701         };
 702                 
 703         private static final CssMetaData<ScrollPane,Boolean> FIT_TO_HEIGHT =
 704             new CssMetaData<ScrollPane, Boolean>("-fx-fit-to-height",
 705                 BooleanConverter.getInstance(), Boolean.FALSE){
 706 
 707             @Override
 708             public boolean isSettable(ScrollPane n) {
 709                 return n.fitToHeight == null || !n.fitToHeight.isBound();
 710             }
 711 
 712             @Override
 713             public StyleableProperty<Boolean> getStyleableProperty(ScrollPane n) {
 714                 return (StyleableProperty<Boolean>)(WritableValue<Boolean>)n.fitToHeightProperty();
 715             }
 716         };
 717                 
 718         private static final CssMetaData<ScrollPane,Boolean> PANNABLE =
 719             new CssMetaData<ScrollPane, Boolean>("-fx-pannable",
 720                 BooleanConverter.getInstance(), Boolean.FALSE){
 721 
 722             @Override
 723             public boolean isSettable(ScrollPane n) {
 724                 return n.pannable == null || !n.pannable.isBound();
 725             }
 726 
 727             @Override
 728             public StyleableProperty<Boolean> getStyleableProperty(ScrollPane n) {
 729                 return (StyleableProperty<Boolean>)(WritableValue<Boolean>)n.pannableProperty();
 730             }
 731         };
 732 
 733         private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
 734         static {
 735             final List<CssMetaData<? extends Styleable, ?>> styleables = 
 736                 new ArrayList<CssMetaData<? extends Styleable, ?>>(Control.getClassCssMetaData());
 737             styleables.add(HBAR_POLICY);
 738             styleables.add(VBAR_POLICY);
 739             styleables.add(FIT_TO_WIDTH);
 740             styleables.add(FIT_TO_HEIGHT);
 741             styleables.add(PANNABLE);
 742             STYLEABLES = Collections.unmodifiableList(styleables);
 743         }
 744     }
 745 
 746     /**
 747      * @return The CssMetaData associated with this class, which may include the
 748      * CssMetaData of its super classes.
 749      * @since JavaFX 8.0
 750      */
 751     public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
 752         return StyleableProperties.STYLEABLES;
 753     }
 754 
 755     /**
 756      * {@inheritDoc}
 757      * @since JavaFX 8.0
 758      */
 759     @Override
 760     public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
 761         return getClassCssMetaData();
 762     }
 763 
 764     private static final PseudoClass PANNABLE_PSEUDOCLASS_STATE =
 765             PseudoClass.getPseudoClass("pannable");
 766     private static final PseudoClass FIT_TO_WIDTH_PSEUDOCLASS_STATE =
 767             PseudoClass.getPseudoClass("fitToWidth");
 768     private static final PseudoClass FIT_TO_HEIGHT_PSEUDOCLASS_STATE =
 769             PseudoClass.getPseudoClass("fitToHeight");
 770     
 771     /**
 772       * Most Controls return true for focusTraversable, so Control overrides
 773       * this method to return true, but ScrollPane returns false for
 774       * focusTraversable's initial value; hence the override of the override. 
 775       * This method is called from CSS code to get the correct initial value.
 776       * @treatAsPrivate implementation detail
 777       */
 778     @Deprecated @Override
 779     protected /*do not make final*/ Boolean impl_cssGetFocusTraversableInitialValue() {
 780         return Boolean.FALSE;
 781     }
 782 
 783 
 784     /***************************************************************************
 785      *                                                                         *
 786      * Accessibility handling                                                  *
 787      *                                                                         *
 788      **************************************************************************/
 789 
 790     @Override
 791     public Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) {
 792         switch (attribute) {
 793             case CONTENTS: return getContent();
 794             default: return super.queryAccessibleAttribute(attribute, parameters);
 795         }
 796     }
 797 
 798 
 799     /***************************************************************************
 800      *                                                                         *
 801      * Support classes                                                         *
 802      *                                                                         *
 803      **************************************************************************/
 804 
 805     /**
 806      * An enumeration denoting the policy to be used by a scrollable
 807      * Control in deciding whether to show a scroll bar.
 808      * @since JavaFX 2.0
 809      */
 810     public static enum ScrollBarPolicy {
 811         /**
 812          * Indicates that a scroll bar should never be shown.
 813          */
 814         NEVER,
 815         /**
 816          * Indicates that a scroll bar should always be shown.
 817          */
 818         ALWAYS,
 819         /**
 820          * Indicates that a scroll bar should be shown when required.
 821          */
 822         AS_NEEDED
 823     }
 824 
 825 
 826 
 827 }