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.property.BooleanProperty;
  33 import javafx.beans.property.DoubleProperty;
  34 import javafx.beans.property.DoublePropertyBase;
  35 import javafx.beans.property.IntegerProperty;
  36 import javafx.beans.property.ObjectProperty;
  37 import javafx.beans.property.SimpleBooleanProperty;
  38 import javafx.beans.property.SimpleObjectProperty;
  39 import javafx.beans.value.WritableValue;
  40 import javafx.geometry.Orientation;
  41 import javafx.scene.AccessibleAction;
  42 import javafx.scene.AccessibleAttribute;
  43 import javafx.scene.AccessibleRole;
  44 import javafx.util.StringConverter;
  45 
  46 import com.sun.javafx.util.Utils;
  47 
  48 import javafx.css.CssMetaData;
  49 import javafx.css.PseudoClass;
  50 import javafx.css.StyleableBooleanProperty;
  51 import javafx.css.StyleableDoubleProperty;
  52 import javafx.css.StyleableIntegerProperty;
  53 import javafx.css.StyleableObjectProperty;
  54 
  55 import com.sun.javafx.css.converters.BooleanConverter;
  56 import com.sun.javafx.css.converters.EnumConverter;
  57 import com.sun.javafx.css.converters.SizeConverter;
  58 import com.sun.javafx.scene.control.skin.SliderSkin;
  59 
  60 import javafx.css.Styleable;
  61 import javafx.css.StyleableProperty;
  62 
  63 /**
  64  * The Slider Control is used to display a continuous or discrete range of
  65  * valid numeric choices and allows the user to interact with the control. It is
  66  * typically represented visually as having a "track" and a "knob" or "thumb"
  67  * which is dragged within the track. The Slider can optionally show tick marks
  68  * and labels indicating the different slider position values.
  69  * <p>
  70  * The three fundamental variables of the slider are <code>min</code>,
  71  * <code>max</code>, and <code>value</code>. The <code>value</code> should always
  72  * be a number within the range defined by <code>min</code> and
  73  * <code>max</code>. <code>min</code> should always be less than or equal to
  74  * <code>max</code> (although a slider who's <code>min</code> and
  75  * <code>max</code> are equal is a degenerate case that makes no sense).
  76  * <code>min</code> defaults to 0, whereas <code>max</code> defaults to 100.
  77  * <p>
  78  * This first example creates a slider who's range, or span, goes from 0 to 1,
  79  * and who's value defaults to .5:
  80  *
  81  * <pre>
  82  * import javafx.scene.control.Slider;
  83  * 
  84  * Slider slider = new Slider(0, 1, 0.5);
  85  * </pre>
  86  * 
  87  * <p>
  88  * This next example shows a slider with customized tick marks and tick mark
  89  * labels, which also spans from 0 to 1:
  90  *
  91  * <pre>
  92  * import javafx.scene.control.Slider;
  93  * 
  94  * Slider slider = new Slider(0, 1, 0.5);
  95  * slider.setShowTickMarks(true);
  96  * slider.setShowTickLabels(true);
  97  * slider.setMajorTickUnit(0.25f);
  98  * slider.setBlockIncrement(0.1f);
  99  * </pre>
 100  * @since JavaFX 2.0
 101  */
 102 public class Slider extends Control {
 103 
 104     /**
 105      * Creates a default Slider instance.
 106      */
 107     public Slider() {
 108         initialize();
 109     }
 110 
 111     /**
 112      * Constructs a Slider control with the specified slider min, max and current value values.
 113      * @param min Slider minimum value
 114      * @param max Slider maximum value
 115      * @param value Slider current value
 116      */
 117     public Slider(double min, double max, double value) {
 118         setMax(max);
 119         setMin(min);
 120         setValue(value);
 121         adjustValues();
 122         initialize();
 123     }
 124 
 125     private void initialize() {
 126         //Initialize the style class to be 'slider'.
 127         getStyleClass().setAll(DEFAULT_STYLE_CLASS);
 128         setAccessibleRole(AccessibleRole.SLIDER);
 129     }
 130     /**
 131      * The maximum value represented by this Slider. This must be a
 132      * value greater than {@link #minProperty() min}.
 133      */
 134     private DoubleProperty max;
 135     public final void setMax(double value) {
 136         maxProperty().set(value);
 137     }
 138 
 139     public final double getMax() {
 140         return max == null ? 100 : max.get();
 141     }
 142 
 143     public final DoubleProperty maxProperty() {
 144         if (max == null) {
 145             max = new DoublePropertyBase(100) {
 146                 @Override protected void invalidated() {
 147                     if (get() < getMin()) {
 148                         setMin(get());
 149                     }
 150                     adjustValues();
 151                     notifyAccessibleAttributeChanged(AccessibleAttribute.MAX_VALUE);
 152                 }
 153 
 154                 @Override
 155                 public Object getBean() {
 156                     return Slider.this;
 157                 }
 158 
 159                 @Override
 160                 public String getName() {
 161                     return "max";
 162                 }
 163             };
 164         }
 165         return max;
 166     }
 167     /**
 168      * The minimum value represented by this Slider. This must be a
 169      * value less than {@link #maxProperty() max}.
 170      */
 171     private DoubleProperty min;
 172     public final void setMin(double value) {
 173         minProperty().set(value);
 174     }
 175 
 176     public final double getMin() {
 177         return min == null ? 0 : min.get();
 178     }
 179 
 180     public final DoubleProperty minProperty() {
 181         if (min == null) {
 182             min = new DoublePropertyBase(0) {
 183                 @Override protected void invalidated() {
 184                     if (get() > getMax()) {
 185                         setMax(get());
 186                     }
 187                     adjustValues();
 188                     notifyAccessibleAttributeChanged(AccessibleAttribute.MIN_VALUE);
 189                 }
 190 
 191                 @Override
 192                 public Object getBean() {
 193                     return Slider.this;
 194                 }
 195 
 196                 @Override
 197                 public String getName() {
 198                     return "min";
 199                 }
 200             };
 201         }
 202         return min;
 203     }
 204     /**
 205      * The current value represented by this Slider. This value must
 206      * always be between {@link #minProperty() min} and {@link #maxProperty() max},
 207      * inclusive. If it is ever out of bounds either due to {@code min} or
 208      * {@code max} changing or due to itself being changed, then it will
 209      * be clamped to always remain valid.
 210      */
 211     private DoubleProperty value;
 212     public final void setValue(double value) {
 213         if (!valueProperty().isBound()) valueProperty().set(value);
 214     }
 215 
 216     public final double getValue() {
 217         return value == null ? 0 : value.get();
 218     }
 219 
 220     public final DoubleProperty valueProperty() {
 221         if (value == null) {
 222             value = new DoublePropertyBase(0) {
 223                 @Override protected void invalidated() {
 224                     adjustValues();
 225                     notifyAccessibleAttributeChanged(AccessibleAttribute.VALUE);
 226                 }
 227 
 228                 @Override
 229                 public Object getBean() {
 230                     return Slider.this;
 231                 }
 232 
 233                 @Override
 234                 public String getName() {
 235                     return "value";
 236                 }
 237             };
 238         }
 239         return value;
 240     }
 241     /**
 242      * When true, indicates the current value of this Slider is changing.
 243      * It provides notification that the value is changing. Once the value is
 244      * computed, it is reset back to false.
 245      */
 246     private BooleanProperty valueChanging;
 247 
 248     public final void setValueChanging(boolean value) {
 249         valueChangingProperty().set(value);
 250     }
 251 
 252     public final boolean isValueChanging() {
 253         return valueChanging == null ? false : valueChanging.get();
 254     }
 255 
 256     public final BooleanProperty valueChangingProperty() {
 257         if (valueChanging == null) {
 258             valueChanging = new SimpleBooleanProperty(this, "valueChanging", false);
 259         }
 260         return valueChanging;
 261     }
 262 //    /**
 263 //     * The {@code span} is the distance, or quantity, between min and max value.
 264 //     * This will be strictly non-negative, since both {@code min} and
 265 //     * {@code max} are forced to maintain a proper relationship.
 266 //     */
 267 //    //    public def span = bind max - min;
 268     
 269     /**
 270      * The orientation of the {@code Slider} can either be horizontal
 271      * or vertical.
 272      */
 273     private ObjectProperty<Orientation> orientation;
 274     public final void setOrientation(Orientation value) {
 275         orientationProperty().set(value);
 276     }
 277 
 278     public final Orientation getOrientation() {
 279         return orientation == null ? Orientation.HORIZONTAL : orientation.get();
 280     }
 281 
 282     public final ObjectProperty<Orientation> orientationProperty() {
 283         if (orientation == null) {
 284             orientation = new StyleableObjectProperty<Orientation>(Orientation.HORIZONTAL) {
 285                 @Override protected void invalidated() {
 286                     final boolean vertical = (get() == Orientation.VERTICAL);
 287                     pseudoClassStateChanged(VERTICAL_PSEUDOCLASS_STATE,    vertical);
 288                     pseudoClassStateChanged(HORIZONTAL_PSEUDOCLASS_STATE, !vertical);
 289                 }
 290                 
 291                 @Override 
 292                 public CssMetaData<Slider,Orientation> getCssMetaData() {
 293                     return StyleableProperties.ORIENTATION;
 294                 }
 295 
 296                 @Override
 297                 public Object getBean() {
 298                     return Slider.this;
 299                 }
 300 
 301                 @Override
 302                 public String getName() {
 303                     return "orientation";
 304                 }
 305             };
 306         }
 307         return orientation;
 308     }
 309     
 310     
 311     /**
 312      * Indicates that the labels for tick marks should be shown. Typically a
 313      * {@link Skin} implementation will only show labels if
 314      * {@link #showTickMarksProperty() showTickMarks} is also true.
 315      */
 316     private BooleanProperty showTickLabels;
 317     public final void setShowTickLabels(boolean value) {
 318         showTickLabelsProperty().set(value);
 319     }
 320 
 321     public final boolean isShowTickLabels() {
 322         return showTickLabels == null ? false : showTickLabels.get();
 323     }
 324 
 325     public final BooleanProperty showTickLabelsProperty() {
 326         if (showTickLabels == null) {
 327             showTickLabels = new StyleableBooleanProperty(false) {
 328 
 329                 
 330                 @Override 
 331                 public CssMetaData<Slider,Boolean> getCssMetaData() {
 332                     return StyleableProperties.SHOW_TICK_LABELS;
 333                 }
 334 
 335                 @Override
 336                 public Object getBean() {
 337                     return Slider.this;
 338                 }
 339 
 340                 @Override
 341                 public String getName() {
 342                     return "showTickLabels";
 343                 }
 344             };
 345         }
 346         return showTickLabels;
 347     }
 348     /**
 349      * Specifies whether the {@link Skin} implementation should show tick marks.
 350      */
 351     private BooleanProperty showTickMarks;
 352     public final void setShowTickMarks(boolean value) {
 353         showTickMarksProperty().set(value);
 354     }
 355 
 356     public final boolean isShowTickMarks() {
 357         return showTickMarks == null ? false : showTickMarks.get();
 358     }
 359 
 360     public final BooleanProperty showTickMarksProperty() {
 361         if (showTickMarks == null) {
 362             showTickMarks = new StyleableBooleanProperty(false) {
 363 
 364                 
 365                 @Override 
 366                 public CssMetaData<Slider,Boolean> getCssMetaData() {
 367                     return StyleableProperties.SHOW_TICK_MARKS;
 368                 }
 369 
 370                 @Override
 371                 public Object getBean() {
 372                     return Slider.this;
 373                 }
 374 
 375                 @Override
 376                 public String getName() {
 377                     return "showTickMarks";
 378                 }
 379             };
 380         }
 381         return showTickMarks;
 382     }
 383     /**
 384      * The unit distance between major tick marks. For example, if
 385      * the {@link #minProperty() min} is 0 and the {@link #maxProperty() max} is 100 and the
 386      * {@link #majorTickUnitProperty() majorTickUnit} is 25, then there would be 5 tick marks: one at
 387      * position 0, one at position 25, one at position 50, one at position
 388      * 75, and a final one at position 100.
 389      * <p>
 390      * This value should be positive and should be a value less than the
 391      * span. Out of range values are essentially the same as disabling
 392      * tick marks.
 393      */
 394     private DoubleProperty majorTickUnit;
 395     public final void setMajorTickUnit(double value) {
 396         if (value <= 0) {
 397             throw new IllegalArgumentException("MajorTickUnit cannot be less than or equal to 0.");
 398         }
 399         majorTickUnitProperty().set(value);
 400     }
 401 
 402     public final double getMajorTickUnit() {
 403         return majorTickUnit == null ? 25 : majorTickUnit.get();
 404     }
 405 
 406     public final DoubleProperty majorTickUnitProperty() {
 407         if (majorTickUnit == null) {
 408             majorTickUnit = new StyleableDoubleProperty(25) {
 409                 @Override
 410                 public void invalidated() {
 411                     if (get() <= 0) {
 412                         throw new IllegalArgumentException("MajorTickUnit cannot be less than or equal to 0.");
 413                     }
 414                 }
 415                 
 416                 @Override 
 417                 public CssMetaData<Slider,Number> getCssMetaData() {
 418                     return StyleableProperties.MAJOR_TICK_UNIT;
 419                 }
 420 
 421                 @Override
 422                 public Object getBean() {
 423                     return Slider.this;
 424                 }
 425 
 426                 @Override
 427                 public String getName() {
 428                     return "majorTickUnit";
 429                 }
 430             };
 431         }
 432         return majorTickUnit;
 433     }
 434     /**
 435      * The number of minor ticks to place between any two major ticks. This
 436      * number should be positive or zero. Out of range values will disable
 437      * disable minor ticks, as will a value of zero.
 438      */
 439     private IntegerProperty minorTickCount;
 440     public final void setMinorTickCount(int value) {
 441         minorTickCountProperty().set(value);
 442     }
 443 
 444     public final int getMinorTickCount() {
 445         return minorTickCount == null ? 3 : minorTickCount.get();
 446     }
 447 
 448     public final IntegerProperty minorTickCountProperty() {
 449         if (minorTickCount == null) {
 450             minorTickCount = new StyleableIntegerProperty(3) {
 451 
 452                 
 453                 @Override 
 454                 public CssMetaData<Slider,Number> getCssMetaData() {
 455                     return StyleableProperties.MINOR_TICK_COUNT;
 456                 }
 457 
 458                 @Override
 459                 public Object getBean() {
 460                     return Slider.this;
 461                 }
 462 
 463                 @Override
 464                 public String getName() {
 465                     return "minorTickCount";
 466                 }
 467             };
 468         }
 469         return minorTickCount;
 470     }
 471     /**
 472      * Indicates whether the {@link #valueProperty() value} of the {@code Slider} should always
 473      * be aligned with the tick marks. This is honored even if the tick marks
 474      * are not shown.
 475      */
 476     private BooleanProperty snapToTicks;
 477     public final void setSnapToTicks(boolean value) {
 478         snapToTicksProperty().set(value);
 479     }
 480 
 481     public final boolean isSnapToTicks() {
 482         return snapToTicks == null ? false : snapToTicks.get();
 483     }
 484 
 485     public final BooleanProperty snapToTicksProperty() {
 486         if (snapToTicks == null) {
 487             snapToTicks = new StyleableBooleanProperty(false) {
 488                 
 489                 @Override 
 490                 public CssMetaData<Slider,Boolean> getCssMetaData() {
 491                     return StyleableProperties.SNAP_TO_TICKS;
 492                 }
 493 
 494                 @Override
 495                 public Object getBean() {
 496                     return Slider.this;
 497                 }
 498 
 499                 @Override
 500                 public String getName() {
 501                     return "snapToTicks";
 502                 }
 503             };
 504         }
 505         return snapToTicks;
 506     }
 507     /**
 508      * A function for formatting the label for a major tick. The number
 509      * representing the major tick will be passed to the function. If this
 510      * function is not specified, then a default function will be used by
 511      * the {@link Skin} implementation.
 512      */
 513     private ObjectProperty<StringConverter<Double>> labelFormatter;
 514 
 515     public final void setLabelFormatter(StringConverter<Double> value) {
 516         labelFormatterProperty().set(value);
 517     }
 518 
 519     public final StringConverter<Double> getLabelFormatter() {
 520         return labelFormatter == null ? null : labelFormatter.get();
 521     }
 522 
 523     public final ObjectProperty<StringConverter<Double>> labelFormatterProperty() {
 524         if (labelFormatter == null) {
 525             labelFormatter = new SimpleObjectProperty<StringConverter<Double>>(this, "labelFormatter");
 526         }
 527         return labelFormatter;
 528     }
 529     /**
 530      * The amount by which to adjust the slider if the track of the slider is
 531      * clicked. This is used when manipulating the slider position using keys. If
 532      * {@link #snapToTicksProperty() snapToTicks} is true then the nearest tick mark to the adjusted
 533      * value will be used.
 534      */
 535     private DoubleProperty blockIncrement;
 536     public final void setBlockIncrement(double value) {
 537         blockIncrementProperty().set(value);
 538     }
 539 
 540     public final double getBlockIncrement() {
 541         return blockIncrement == null ? 10 : blockIncrement.get();
 542     }
 543 
 544     public final DoubleProperty blockIncrementProperty() {
 545         if (blockIncrement == null) {
 546             blockIncrement = new StyleableDoubleProperty(10) {
 547 
 548                 @Override 
 549                 public CssMetaData<Slider,Number> getCssMetaData() {
 550                     return StyleableProperties.BLOCK_INCREMENT;
 551                 }
 552 
 553                 @Override
 554                 public Object getBean() {
 555                     return Slider.this;
 556                 }
 557 
 558                 @Override
 559                 public String getName() {
 560                     return "blockIncrement";
 561                 }
 562             };
 563         }
 564         return blockIncrement;
 565     }
 566 
 567     /**
 568      * Adjusts {@link #valueProperty() value} to match <code>newValue</code>. The 
 569      * <code>value</code>is the actual amount between the 
 570      * {@link #minProperty() min} and {@link #maxProperty() max}. This function
 571      * also takes into account {@link #snapToTicksProperty() snapToTicks}, which
 572      * is the main difference between adjustValue and setValue. It also ensures 
 573      * that the value is some valid number between min and max.
 574      *
 575      * @expert This function is intended to be used by experts, primarily
 576      *         by those implementing new Skins or Behaviors. It is not common
 577      *         for developers or designers to access this function directly.
 578      */
 579     public void adjustValue(double newValue) {
 580         // figure out the "value" associated with the specified position
 581         final double _min = getMin();
 582         final double _max = getMax();
 583         if (_max <= _min) return;
 584         newValue = newValue < _min ? _min : newValue;
 585         newValue = newValue > _max ? _max : newValue;
 586 
 587         setValue(snapValueToTicks(newValue));
 588     }
 589 
 590     /**
 591      * Increments the value by {@link #blockIncrementProperty() blockIncrement}, bounded by max. If the
 592      * max is less than or equal to the min, then this method does nothing.
 593      */
 594     public void increment() {
 595         adjustValue(getValue() + getBlockIncrement());
 596     }
 597     
 598     /**
 599      * Decrements the value by {@link #blockIncrementProperty() blockIncrement}, bounded by max. If the
 600      * max is less than or equal to the min, then this method does nothing.
 601      */
 602     public void decrement() {
 603         adjustValue(getValue() - getBlockIncrement());
 604     }
 605     
 606     /**
 607      * Ensures that min is always < max, that value is always
 608      * somewhere between the two, and that if snapToTicks is set then the
 609      * value will always be set to align with a tick mark.
 610      */
 611     private void adjustValues() {
 612         if ((getValue() < getMin() || getValue() > getMax()) /* &&  !isReadOnly(value)*/)
 613              setValue(Utils.clamp(getMin(), getValue(), getMax()));
 614     }
 615 
 616      /**
 617      * Utility function which, given the specified value, will position it
 618      * either aligned with a tick, or simply clamp between min & max value,
 619      * depending on whether snapToTicks is set.
 620      *
 621      * @expert This function is intended to be used by experts, primarily
 622      *         by those implementing new Skins or Behaviors. It is not common
 623      *         for developers or designers to access this function directly.
 624      */
 625     private double snapValueToTicks(double val) {
 626         double v = val;
 627         if (isSnapToTicks()) {
 628             double tickSpacing = 0;
 629             // compute the nearest tick to this value
 630             if (getMinorTickCount() != 0) {
 631                 tickSpacing = getMajorTickUnit() / (Math.max(getMinorTickCount(),0)+1);
 632             } else {
 633                 tickSpacing = getMajorTickUnit();
 634             }
 635             int prevTick = (int)((v - getMin())/ tickSpacing);
 636             double prevTickValue = (prevTick) * tickSpacing + getMin();
 637             double nextTickValue = (prevTick + 1) * tickSpacing + getMin();
 638             v = Utils.nearest(prevTickValue, v, nextTickValue);
 639         }
 640         return Utils.clamp(getMin(), v, getMax());
 641     }
 642 
 643     /** {@inheritDoc} */
 644     @Override protected Skin<?> createDefaultSkin() {
 645         return new SliderSkin(this);
 646     }
 647 
 648     /***************************************************************************
 649      *                                                                         *
 650      *                         Stylesheet Handling                             *
 651      *                                                                         *
 652      **************************************************************************/
 653 
 654     private static final String DEFAULT_STYLE_CLASS = "slider";
 655 
 656     private static class StyleableProperties {
 657         private static final CssMetaData<Slider,Number> BLOCK_INCREMENT =
 658             new CssMetaData<Slider,Number>("-fx-block-increment",
 659                 SizeConverter.getInstance(), 10.0) {
 660 
 661             @Override
 662             public boolean isSettable(Slider n) {
 663                 return n.blockIncrement == null || !n.blockIncrement.isBound();
 664             }
 665 
 666             @Override
 667             public StyleableProperty<Number> getStyleableProperty(Slider n) {
 668                 return (StyleableProperty<Number>)(WritableValue<Number>)n.blockIncrementProperty();
 669             }
 670         };
 671         
 672         private static final CssMetaData<Slider,Boolean> SHOW_TICK_LABELS =
 673             new CssMetaData<Slider,Boolean>("-fx-show-tick-labels",
 674                 BooleanConverter.getInstance(), Boolean.FALSE) {
 675 
 676             @Override
 677             public boolean isSettable(Slider n) {
 678                 return n.showTickLabels == null || !n.showTickLabels.isBound();
 679             }
 680 
 681             @Override
 682             public StyleableProperty<Boolean> getStyleableProperty(Slider n) {
 683                 return (StyleableProperty<Boolean>)(WritableValue<Boolean>)n.showTickLabelsProperty();
 684             }
 685         };
 686                     
 687         private static final CssMetaData<Slider,Boolean> SHOW_TICK_MARKS =
 688             new CssMetaData<Slider,Boolean>("-fx-show-tick-marks",
 689                 BooleanConverter.getInstance(), Boolean.FALSE) {
 690 
 691             @Override
 692             public boolean isSettable(Slider n) {
 693                 return n.showTickMarks == null || !n.showTickMarks.isBound();
 694             }
 695 
 696             @Override
 697             public StyleableProperty<Boolean> getStyleableProperty(Slider n) {
 698                 return (StyleableProperty<Boolean>)(WritableValue<Boolean>)n.showTickMarksProperty();
 699             }
 700         };
 701             
 702         private static final CssMetaData<Slider,Boolean> SNAP_TO_TICKS =
 703             new CssMetaData<Slider,Boolean>("-fx-snap-to-ticks",
 704                 BooleanConverter.getInstance(), Boolean.FALSE) {
 705 
 706             @Override
 707             public boolean isSettable(Slider n) {
 708                 return n.snapToTicks == null || !n.snapToTicks.isBound();
 709             }
 710 
 711             @Override
 712             public StyleableProperty<Boolean> getStyleableProperty(Slider n) {
 713                 return (StyleableProperty<Boolean>)(WritableValue<Boolean>)n.snapToTicksProperty();
 714             }
 715         };
 716         
 717         private static final CssMetaData<Slider,Number> MAJOR_TICK_UNIT =
 718             new CssMetaData<Slider,Number>("-fx-major-tick-unit",
 719                 SizeConverter.getInstance(), 25.0) {
 720 
 721             @Override
 722             public boolean isSettable(Slider n) {
 723                 return n.majorTickUnit == null || !n.majorTickUnit.isBound();
 724             }
 725 
 726             @Override
 727             public StyleableProperty<Number> getStyleableProperty(Slider n) {
 728                 return (StyleableProperty<Number>)(WritableValue<Number>)n.majorTickUnitProperty();
 729             }
 730         };
 731         
 732         private static final CssMetaData<Slider,Number> MINOR_TICK_COUNT =
 733             new CssMetaData<Slider,Number>("-fx-minor-tick-count",
 734                 SizeConverter.getInstance(), 3.0) {
 735 
 736             @Override
 737             public boolean isSettable(Slider n) {
 738                 return n.minorTickCount == null || !n.minorTickCount.isBound();
 739             }
 740 
 741             @Override
 742             public StyleableProperty<Number> getStyleableProperty(Slider n) {
 743                 return (StyleableProperty<Number>)(WritableValue<Number>)n.minorTickCountProperty();
 744             }
 745         };
 746         
 747         private static final CssMetaData<Slider,Orientation> ORIENTATION =
 748             new CssMetaData<Slider,Orientation>("-fx-orientation",
 749                 new EnumConverter<Orientation>(Orientation.class), 
 750                 Orientation.HORIZONTAL) {
 751 
 752             @Override
 753             public Orientation getInitialValue(Slider node) {
 754                 // A vertical Slider should remain vertical 
 755                 return node.getOrientation();
 756             }
 757 
 758             @Override
 759             public boolean isSettable(Slider n) {
 760                 return n.orientation == null || !n.orientation.isBound();
 761             }
 762 
 763             @Override
 764             public StyleableProperty<Orientation> getStyleableProperty(Slider n) {
 765                 return (StyleableProperty<Orientation>)(WritableValue<Orientation>)n.orientationProperty();
 766             }
 767         };
 768 
 769         private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
 770         static {
 771             final List<CssMetaData<? extends Styleable, ?>> styleables = 
 772                 new ArrayList<CssMetaData<? extends Styleable, ?>>(Control.getClassCssMetaData());
 773             styleables.add(BLOCK_INCREMENT);
 774             styleables.add(SHOW_TICK_LABELS);
 775             styleables.add(SHOW_TICK_MARKS);
 776             styleables.add(SNAP_TO_TICKS);
 777             styleables.add(MAJOR_TICK_UNIT);
 778             styleables.add(MINOR_TICK_COUNT);
 779             styleables.add(ORIENTATION);
 780             
 781             STYLEABLES = Collections.unmodifiableList(styleables);
 782         }
 783     }
 784 
 785     /**
 786      * @return The CssMetaData associated with this class, which may include the
 787      * CssMetaData of its super classes.
 788      * @since JavaFX 8.0
 789      */
 790     public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
 791         return StyleableProperties.STYLEABLES;
 792     }
 793 
 794     /**
 795      * RT-19263
 796      * @treatAsPrivate implementation detail
 797      * @deprecated This is an experimental API that is not intended for general use and is subject to change in future versions
 798      * @since JavaFX 8.0
 799      */
 800     @Deprecated
 801     @Override protected List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
 802         return getClassCssMetaData();
 803     }
 804 
 805     private static final PseudoClass VERTICAL_PSEUDOCLASS_STATE =
 806             PseudoClass.getPseudoClass("vertical");
 807     private static final PseudoClass HORIZONTAL_PSEUDOCLASS_STATE =
 808             PseudoClass.getPseudoClass("horizontal");
 809 
 810 
 811 
 812     /***************************************************************************
 813      *                                                                         *
 814      * Accessibility handling                                                  *
 815      *                                                                         *
 816      **************************************************************************/
 817 
 818     @Override
 819     public Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) {
 820         switch (attribute) {
 821             case VALUE: return getValue();
 822             case MAX_VALUE: return getMax();
 823             case MIN_VALUE: return getMin();
 824             case ORIENTATION: return getOrientation();
 825             default: return super.queryAccessibleAttribute(attribute, parameters);
 826         }
 827     }
 828 
 829     @Override
 830     public void executeAccessibleAction(AccessibleAction action, Object... parameters) {
 831         switch (action) {
 832             case INCREMENT: increment(); break;
 833             case DECREMENT: decrement(); break;
 834             case SET_VALUE: {
 835                 Double value = (Double) parameters[0];
 836                 if (value != null) setValue(value);
 837                 break;
 838             }
 839             default: super.executeAccessibleAction(action, parameters);
 840         }
 841     }
 842 }