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

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


   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 com.sun.javafx.scene.control.skin;
  27 

  28 import javafx.animation.Transition;
  29 import javafx.geometry.Orientation;
  30 import javafx.geometry.Point2D;
  31 import javafx.geometry.Side;
  32 import javafx.scene.AccessibleAttribute;
  33 import javafx.scene.AccessibleRole;

  34 import javafx.scene.chart.NumberAxis;




  35 import javafx.scene.control.Slider;
  36 import javafx.scene.layout.StackPane;
  37 import javafx.util.Duration;
  38 import javafx.util.StringConverter;
  39 
  40 import com.sun.javafx.scene.control.behavior.SliderBehavior;
  41 
  42 /**
  43  * Region/css based skin for Slider
  44 */
  45 public class SliderSkin extends BehaviorSkinBase<Slider, SliderBehavior> {









  46 
  47     /** Track if slider is vertical/horizontal and cause re layout */
  48 //    private boolean horizontal;
  49     private NumberAxis tickLine = null;
  50     private double trackToTickGap = 2;
  51 
  52     private boolean showTickMarks;
  53     private double thumbWidth;
  54     private double thumbHeight;
  55 
  56     private double trackStart;
  57     private double trackLength;
  58     private double thumbTop;
  59     private double thumbLeft;
  60     private double preDragThumbPos;
  61     private Point2D dragStart; // in skin coordinates
  62 
  63     private StackPane thumb;
  64     private StackPane track;
  65     private boolean trackClicked = false;
  66 //    private double visibleAmount = 16;
  67 
  68     public SliderSkin(Slider slider) {
  69         super(slider, new SliderBehavior(slider));
  70 
  71         initialize();
  72         slider.requestLayout();
  73         registerChangeListener(slider.minProperty(), "MIN");
  74         registerChangeListener(slider.maxProperty(), "MAX");
  75         registerChangeListener(slider.valueProperty(), "VALUE");
  76         registerChangeListener(slider.orientationProperty(), "ORIENTATION");
  77         registerChangeListener(slider.showTickMarksProperty(), "SHOW_TICK_MARKS");
  78         registerChangeListener(slider.showTickLabelsProperty(), "SHOW_TICK_LABELS");
  79         registerChangeListener(slider.majorTickUnitProperty(), "MAJOR_TICK_UNIT");
  80         registerChangeListener(slider.minorTickCountProperty(), "MINOR_TICK_COUNT");
  81         registerChangeListener(slider.labelFormatterProperty(), "TICK_LABEL_FORMATTER");
  82     }
  83 
  84     private void initialize() {
  85         thumb = new StackPane() {
  86             @Override
  87             public Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) {
  88                 switch (attribute) {
  89                     case VALUE: return getSkinnable().getValue();
  90                     default: return super.queryAccessibleAttribute(attribute, parameters);
  91                 }
  92             }
  93         };
  94         thumb.getStyleClass().setAll("thumb");
  95         thumb.setAccessibleRole(AccessibleRole.THUMB);
  96         track = new StackPane();
  97         track.getStyleClass().setAll("track");
  98 //        horizontal = getSkinnable().isVertical();
  99 
 100         getChildren().clear();
 101         getChildren().addAll(track, thumb);
 102         setShowTickMarks(getSkinnable().isShowTickMarks(), getSkinnable().isShowTickLabels());
 103         track.setOnMousePressed(me -> {
 104             if (!thumb.isPressed()) {
 105                 trackClicked = true;
 106                 if (getSkinnable().getOrientation() == Orientation.HORIZONTAL) {
 107                     getBehavior().trackPress(me, (me.getX() / trackLength));
 108                 } else {
 109                     getBehavior().trackPress(me, (me.getY() / trackLength));
 110                 }
 111                 trackClicked = false;
 112             }
 113         });
 114         
 115         track.setOnMouseDragged(me -> {
 116             if (!thumb.isPressed()) {
 117                 if (getSkinnable().getOrientation() == Orientation.HORIZONTAL) {
 118                     getBehavior().trackPress(me, (me.getX() / trackLength));
 119                 } else {
 120                     getBehavior().trackPress(me, (me.getY() / trackLength));
 121                 }
 122             }
 123         });
 124 
 125         thumb.setOnMousePressed(me -> {
 126             getBehavior().thumbPressed(me, 0.0f);
 127             dragStart = thumb.localToParent(me.getX(), me.getY());
 128             preDragThumbPos = (getSkinnable().getValue() - getSkinnable().getMin()) /
 129                     (getSkinnable().getMax() - getSkinnable().getMin());
 130         });
 131 
 132         thumb.setOnMouseReleased(me -> {
 133             getBehavior().thumbReleased(me);
 134         });
 135 
 136         thumb.setOnMouseDragged(me -> {
 137             Point2D cur = thumb.localToParent(me.getX(), me.getY());
 138             double dragPos = (getSkinnable().getOrientation() == Orientation.HORIZONTAL)?
 139                 cur.getX() - dragStart.getX() : -(cur.getY() - dragStart.getY());
 140             getBehavior().thumbDragged(me, preDragThumbPos + dragPos / trackLength);
 141         });
 142     }
 143 
 144     StringConverter<Number> stringConverterWrapper = new StringConverter<Number>() {
 145         Slider slider = getSkinnable();
 146         @Override public String toString(Number object) {
 147             return(object != null) ? slider.getLabelFormatter().toString(object.doubleValue()) : "";
 148         }
 149         @Override public Number fromString(String string) {
 150             return slider.getLabelFormatter().fromString(string);
 151         }
 152     };
 153     
 154      private void setShowTickMarks(boolean ticksVisible, boolean labelsVisible) {
 155         showTickMarks = (ticksVisible || labelsVisible);
 156         Slider slider = getSkinnable();
 157         if (showTickMarks) {
 158             if (tickLine == null) {
 159                 tickLine = new NumberAxis();
 160                 tickLine.setAutoRanging(false);
 161                 tickLine.setSide(slider.getOrientation() == Orientation.VERTICAL ? Side.RIGHT : (slider.getOrientation() == null) ? Side.RIGHT: Side.BOTTOM);
 162                 tickLine.setUpperBound(slider.getMax());
 163                 tickLine.setLowerBound(slider.getMin());
 164                 tickLine.setTickUnit(slider.getMajorTickUnit());
 165                 tickLine.setTickMarkVisible(ticksVisible);
 166                 tickLine.setTickLabelsVisible(labelsVisible);
 167                 tickLine.setMinorTickVisible(ticksVisible);
 168                 // add 1 to the slider minor tick count since the axis draws one
 169                 // less minor ticks than the number given.
 170                 tickLine.setMinorTickCount(Math.max(slider.getMinorTickCount(),0) + 1);
 171                 if (slider.getLabelFormatter() != null) {
 172                     tickLine.setTickLabelFormatter(stringConverterWrapper);
 173                 }
 174                 getChildren().clear();
 175                 getChildren().addAll(tickLine, track, thumb);
 176             } else {
 177                 tickLine.setTickLabelsVisible(labelsVisible);
 178                 tickLine.setTickMarkVisible(ticksVisible);
 179                 tickLine.setMinorTickVisible(ticksVisible);
 180             }
 181         } 
 182         else  {
 183             getChildren().clear();
 184             getChildren().addAll(track, thumb);
 185 //            tickLine = null;
 186         }
 187 
 188         getSkinnable().requestLayout();
 189     }
 190 
 191     @Override protected void handleControlPropertyChanged(String p) {
 192         super.handleControlPropertyChanged(p);
 193         Slider slider = getSkinnable();
 194         if ("ORIENTATION".equals(p)) {


















 195             if (showTickMarks && tickLine != null) {
 196                 tickLine.setSide(slider.getOrientation() == Orientation.VERTICAL ? Side.RIGHT : (slider.getOrientation() == null) ? Side.RIGHT: Side.BOTTOM);
 197             }
 198             getSkinnable().requestLayout();
 199         } else if ("VALUE".equals(p)) {
 200             // only animate thumb if the track was clicked - not if the thumb is dragged
 201             positionThumb(trackClicked);
 202         } else if ("MIN".equals(p) ) {
 203             if (showTickMarks && tickLine != null) {
 204                 tickLine.setLowerBound(slider.getMin());
 205             }
 206             getSkinnable().requestLayout();
 207         } else if ("MAX".equals(p)) {





 208             if (showTickMarks && tickLine != null) {
 209                 tickLine.setUpperBound(slider.getMax());
 210             }
 211             getSkinnable().requestLayout();
 212         } else if ("SHOW_TICK_MARKS".equals(p) || "SHOW_TICK_LABELS".equals(p)) {
 213             setShowTickMarks(slider.isShowTickMarks(), slider.isShowTickLabels());
 214         }  else if ("MAJOR_TICK_UNIT".equals(p)) {

 215             if (tickLine != null) {
 216                 tickLine.setTickUnit(slider.getMajorTickUnit());
 217                 getSkinnable().requestLayout();
 218             }
 219         } else if ("MINOR_TICK_COUNT".equals(p)) {

 220             if (tickLine != null) {
 221                 tickLine.setMinorTickCount(Math.max(slider.getMinorTickCount(),0) + 1);
 222                 getSkinnable().requestLayout();
 223             }
 224         } else if ("TICK_LABEL_FORMATTER".equals(p)) {

 225             if (tickLine != null) {
 226                 if (slider.getLabelFormatter() == null) {
 227                     tickLine.setTickLabelFormatter(null);
 228                 } else {
 229                     tickLine.setTickLabelFormatter(stringConverterWrapper);
 230                     tickLine.requestAxisLayout();
 231                 }
 232             }
 233         }
 234     }
 235 
 236     /**
 237      * Called when ever either min, max or value changes, so thumb's layoutX, Y is recomputed.
 238      */
 239     void positionThumb(final boolean animate) {
 240         Slider s = getSkinnable();
 241         if (s.getValue() > s.getMax()) return;// this can happen if we are bound to something 
 242         boolean horizontal = s.getOrientation() == Orientation.HORIZONTAL;
 243         final double endX = (horizontal) ? trackStart + (((trackLength * ((s.getValue() - s.getMin()) /
 244                 (s.getMax() - s.getMin()))) - thumbWidth/2)) : thumbLeft;
 245         final double endY = (horizontal) ? thumbTop :
 246             snappedTopInset() + trackLength - (trackLength * ((s.getValue() - s.getMin()) /
 247                 (s.getMax() - s.getMin()))); //  - thumbHeight/2
 248         
 249         if (animate) {
 250             // lets animate the thumb transition
 251             final double startX = thumb.getLayoutX();
 252             final double startY = thumb.getLayoutY();
 253             Transition transition = new Transition() {
 254                 {
 255                     setCycleDuration(Duration.millis(200));
 256                 }
 257 
 258                 @Override protected void interpolate(double frac) {
 259                     if (!Double.isNaN(startX)) {
 260                         thumb.setLayoutX(startX + frac * (endX - startX));
 261                     }
 262                     if (!Double.isNaN(startY)) {
 263                         thumb.setLayoutY(startY + frac * (endY - startY));
 264                     }
 265                 }
 266             };
 267             transition.play();
 268         } else {
 269             thumb.setLayoutX(endX);
 270             thumb.setLayoutY(endY);
 271         }
 272     }
 273 

 274     @Override protected void layoutChildren(final double x, final double y,
 275             final double w, final double h) {
 276          // calculate the available space
 277         // resize thumb to preferred size
 278         thumbWidth = snapSize(thumb.prefWidth(-1));
 279         thumbHeight = snapSize(thumb.prefHeight(-1));
 280         thumb.resize(thumbWidth, thumbHeight);
 281         // we are assuming the is common radius's for all corners on the track
 282         double trackRadius = track.getBackground() == null ? 0 : track.getBackground().getFills().size() > 0 ?
 283                 track.getBackground().getFills().get(0).getRadii().getTopLeftHorizontalRadius() : 0;
 284 
 285         if (getSkinnable().getOrientation() == Orientation.HORIZONTAL) {
 286             double tickLineHeight =  (showTickMarks) ? tickLine.prefHeight(-1) : 0;
 287             double trackHeight = snapSize(track.prefHeight(-1));
 288             double trackAreaHeight = Math.max(trackHeight,thumbHeight);
 289             double totalHeightNeeded = trackAreaHeight  + ((showTickMarks) ? trackToTickGap+tickLineHeight : 0);
 290             double startY = y + ((h - totalHeightNeeded)/2); // center slider in available height vertically
 291             trackLength = snapSize(w - thumbWidth);
 292             trackStart = snapPosition(x + (thumbWidth/2));
 293             double trackTop = (int)(startY + ((trackAreaHeight-trackHeight)/2));


 328             track.resizeRelocate(trackLeft,
 329                                  (int)(trackStart - trackRadius),
 330                                  trackWidth,
 331                                  (int)(trackLength + trackRadius + trackRadius));
 332             // layout tick line
 333             if (showTickMarks) {
 334                 tickLine.setLayoutX(trackLeft+trackWidth+trackToTickGap);
 335                 tickLine.setLayoutY(trackStart);
 336                 tickLine.resize(tickLineWidth, trackLength);
 337                 tickLine.requestAxisLayout();
 338             } else {
 339                 if (tickLine != null) {
 340                     tickLine.resize(0,0);
 341                     tickLine.requestAxisLayout();
 342                 }
 343                 tickLine = null;
 344             }
 345         }
 346     }
 347 
 348     double minTrackLength() {
 349         return 2*thumb.prefWidth(-1);
 350     }
 351 
 352     @Override protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
 353         final Slider s = getSkinnable();
 354         if (s.getOrientation() == Orientation.HORIZONTAL) {
 355             return (leftInset + minTrackLength() + thumb.minWidth(-1) + rightInset);
 356         } else {
 357             return(leftInset + thumb.prefWidth(-1) + rightInset);
 358         }
 359     }
 360 

 361     @Override protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
 362         final Slider s = getSkinnable();
 363         if (s.getOrientation() == Orientation.HORIZONTAL) {
 364             double axisHeight = showTickMarks ? (tickLine.prefHeight(-1) + trackToTickGap) : 0;
 365             return topInset + thumb.prefHeight(-1) + axisHeight + bottomInset;
 366         } else {
 367             return topInset + minTrackLength() + thumb.prefHeight(-1) + bottomInset;
 368         }
 369     }
 370 

 371     @Override protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
 372         final Slider s = getSkinnable();
 373         if (s.getOrientation() == Orientation.HORIZONTAL) {
 374             if(showTickMarks) {
 375                 return Math.max(140, tickLine.prefWidth(-1));
 376             } else {
 377                 return 140;
 378             }
 379         } else {
 380             double axisWidth = showTickMarks ? (tickLine.prefWidth(-1) + trackToTickGap) : 0;
 381             return leftInset + Math.max(thumb.prefWidth(-1), track.prefWidth(-1)) + axisWidth + rightInset;
 382         }
 383     }
 384 

 385     @Override protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
 386         final Slider s = getSkinnable();
 387         if (s.getOrientation() == Orientation.HORIZONTAL) {
 388             return topInset + Math.max(thumb.prefHeight(-1), track.prefHeight(-1)) +
 389              ((showTickMarks) ? (trackToTickGap+tickLine.prefHeight(-1)) : 0)  + bottomInset;
 390         } else {
 391             if(showTickMarks) {
 392                 return Math.max(140, tickLine.prefHeight(-1));
 393             } else {
 394                 return 140;
 395             }
 396         }
 397     }
 398 

 399     @Override protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
 400         if (getSkinnable().getOrientation() == Orientation.HORIZONTAL) {
 401             return Double.MAX_VALUE;
 402         } else {
 403             return getSkinnable().prefWidth(-1);
 404         }
 405     }
 406 

 407     @Override protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
 408         if (getSkinnable().getOrientation() == Orientation.HORIZONTAL) {
 409             return getSkinnable().prefHeight(width);
 410         } else {
 411             return Double.MAX_VALUE;
 412         }



















































































































































 413     }
 414 }
 415 


   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.skin;
  27 
  28 import com.sun.javafx.scene.control.behavior.BehaviorBase;
  29 import javafx.animation.Transition;
  30 import javafx.geometry.Orientation;
  31 import javafx.geometry.Point2D;
  32 import javafx.geometry.Side;
  33 import javafx.scene.AccessibleAttribute;
  34 import javafx.scene.AccessibleRole;
  35 import javafx.scene.Node;
  36 import javafx.scene.chart.NumberAxis;
  37 import javafx.scene.control.Accordion;
  38 import javafx.scene.control.Button;
  39 import javafx.scene.control.Control;
  40 import javafx.scene.control.SkinBase;
  41 import javafx.scene.control.Slider;
  42 import javafx.scene.layout.StackPane;
  43 import javafx.util.Duration;
  44 import javafx.util.StringConverter;
  45 
  46 import com.sun.javafx.scene.control.behavior.SliderBehavior;
  47 
  48 /**
  49  * Default skin implementation for the {@link Slider} control.
  50  *
  51  * @see Slider
  52  * @since 9
  53  */
  54 public class SliderSkin extends SkinBase<Slider> {
  55 
  56     /***************************************************************************
  57      *                                                                         *
  58      * Private fields                                                          *
  59      *                                                                         *
  60      **************************************************************************/
  61 
  62     /** Track if slider is vertical/horizontal and cause re layout */
  63 //    private boolean horizontal;
  64     private NumberAxis tickLine = null;
  65     private double trackToTickGap = 2;
  66 
  67     private boolean showTickMarks;
  68     private double thumbWidth;
  69     private double thumbHeight;
  70 
  71     private double trackStart;
  72     private double trackLength;
  73     private double thumbTop;
  74     private double thumbLeft;
  75     private double preDragThumbPos;
  76     private Point2D dragStart; // in skin coordinates
  77 
  78     private StackPane thumb;
  79     private StackPane track;
  80     private boolean trackClicked = false;
  81 //    private double visibleAmount = 16;
  82 
  83     private final SliderBehavior behavior;










































































  84 
  85     StringConverter<Number> stringConverterWrapper = new StringConverter<Number>() {
  86         Slider slider = getSkinnable();
  87         @Override public String toString(Number object) {
  88             return(object != null) ? slider.getLabelFormatter().toString(object.doubleValue()) : "";
  89         }
  90         @Override public Number fromString(String string) {
  91             return slider.getLabelFormatter().fromString(string);
  92         }
  93     };
  94 

































  95 


  96 
  97     /***************************************************************************
  98      *                                                                         *
  99      * Constructors                                                            *
 100      *                                                                         *
 101      **************************************************************************/
 102 
 103     /**
 104      * Creates a new SliderSkin instance, installing the necessary child
 105      * nodes into the Control {@link Control#getChildren() children} list, as
 106      * well as the necessary input mappings for handling key, mouse, etc events.
 107      *
 108      * @param control The control that this skin should be installed onto.
 109      */
 110     public SliderSkin(Slider control) {
 111         super(control);
 112 
 113         behavior = new SliderBehavior(control);
 114 //        control.setInputMap(behavior.getInputMap());
 115 
 116         initialize();
 117         control.requestLayout();
 118         registerChangeListener(control.minProperty(), e -> {
 119             if (showTickMarks && tickLine != null) {
 120                 tickLine.setLowerBound(control.getMin());
 121             }
 122             getSkinnable().requestLayout();
 123         });
 124         registerChangeListener(control.maxProperty(), e -> {


 125             if (showTickMarks && tickLine != null) {
 126                 tickLine.setUpperBound(control.getMax());
 127             }
 128             getSkinnable().requestLayout();
 129         });
 130         registerChangeListener(control.valueProperty(), e -> {
 131             // only animate thumb if the track was clicked - not if the thumb is dragged
 132             positionThumb(trackClicked);
 133         });
 134         registerChangeListener(control.orientationProperty(), e -> {
 135             if (showTickMarks && tickLine != null) {
 136                 tickLine.setSide(control.getOrientation() == Orientation.VERTICAL ? Side.RIGHT : (control.getOrientation() == null) ? Side.RIGHT: Side.BOTTOM);
 137             }
 138             getSkinnable().requestLayout();
 139         });
 140         registerChangeListener(control.showTickMarksProperty(), e -> setShowTickMarks(control.isShowTickMarks(), control.isShowTickLabels()));
 141         registerChangeListener(control.showTickLabelsProperty(), e -> setShowTickMarks(control.isShowTickMarks(), control.isShowTickLabels()));
 142         registerChangeListener(control.majorTickUnitProperty(), e -> {
 143             if (tickLine != null) {
 144                 tickLine.setTickUnit(control.getMajorTickUnit());
 145                 getSkinnable().requestLayout();
 146             }
 147         });
 148         registerChangeListener(control.minorTickCountProperty(), e -> {
 149             if (tickLine != null) {
 150                 tickLine.setMinorTickCount(Math.max(control.getMinorTickCount(), 0) + 1);
 151                 getSkinnable().requestLayout();
 152             }
 153         });
 154         registerChangeListener(control.labelFormatterProperty(), e -> {
 155             if (tickLine != null) {
 156                 if (control.getLabelFormatter() == null) {
 157                     tickLine.setTickLabelFormatter(null);
 158                 } else {
 159                     tickLine.setTickLabelFormatter(stringConverterWrapper);
 160                     tickLine.requestAxisLayout();
 161                 }
 162             }
 163         });
 164     }
 165 












 166 








 167 
 168     /***************************************************************************
 169      *                                                                         *
 170      * Public API                                                              *
 171      *                                                                         *
 172      **************************************************************************/
 173 
 174     /** {@inheritDoc} */
 175     @Override public void dispose() {
 176         super.dispose();
 177 
 178         if (behavior != null) {
 179             behavior.dispose();

 180         }
 181     }
 182 
 183     /** {@inheritDoc} */
 184     @Override protected void layoutChildren(final double x, final double y,
 185                                             final double w, final double h) {
 186         // calculate the available space
 187         // resize thumb to preferred size
 188         thumbWidth = snapSize(thumb.prefWidth(-1));
 189         thumbHeight = snapSize(thumb.prefHeight(-1));
 190         thumb.resize(thumbWidth, thumbHeight);
 191         // we are assuming the is common radius's for all corners on the track
 192         double trackRadius = track.getBackground() == null ? 0 : track.getBackground().getFills().size() > 0 ?
 193                 track.getBackground().getFills().get(0).getRadii().getTopLeftHorizontalRadius() : 0;
 194 
 195         if (getSkinnable().getOrientation() == Orientation.HORIZONTAL) {
 196             double tickLineHeight =  (showTickMarks) ? tickLine.prefHeight(-1) : 0;
 197             double trackHeight = snapSize(track.prefHeight(-1));
 198             double trackAreaHeight = Math.max(trackHeight,thumbHeight);
 199             double totalHeightNeeded = trackAreaHeight  + ((showTickMarks) ? trackToTickGap+tickLineHeight : 0);
 200             double startY = y + ((h - totalHeightNeeded)/2); // center slider in available height vertically
 201             trackLength = snapSize(w - thumbWidth);
 202             trackStart = snapPosition(x + (thumbWidth/2));
 203             double trackTop = (int)(startY + ((trackAreaHeight-trackHeight)/2));


 238             track.resizeRelocate(trackLeft,
 239                     (int)(trackStart - trackRadius),
 240                     trackWidth,
 241                     (int)(trackLength + trackRadius + trackRadius));
 242             // layout tick line
 243             if (showTickMarks) {
 244                 tickLine.setLayoutX(trackLeft+trackWidth+trackToTickGap);
 245                 tickLine.setLayoutY(trackStart);
 246                 tickLine.resize(tickLineWidth, trackLength);
 247                 tickLine.requestAxisLayout();
 248             } else {
 249                 if (tickLine != null) {
 250                     tickLine.resize(0,0);
 251                     tickLine.requestAxisLayout();
 252                 }
 253                 tickLine = null;
 254             }
 255         }
 256     }
 257 
 258     /** {@inheritDoc} */



 259     @Override protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
 260         final Slider s = getSkinnable();
 261         if (s.getOrientation() == Orientation.HORIZONTAL) {
 262             return (leftInset + minTrackLength() + thumb.minWidth(-1) + rightInset);
 263         } else {
 264             return(leftInset + thumb.prefWidth(-1) + rightInset);
 265         }
 266     }
 267 
 268     /** {@inheritDoc} */
 269     @Override protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
 270         final Slider s = getSkinnable();
 271         if (s.getOrientation() == Orientation.HORIZONTAL) {
 272             double axisHeight = showTickMarks ? (tickLine.prefHeight(-1) + trackToTickGap) : 0;
 273             return topInset + thumb.prefHeight(-1) + axisHeight + bottomInset;
 274         } else {
 275             return topInset + minTrackLength() + thumb.prefHeight(-1) + bottomInset;
 276         }
 277     }
 278 
 279     /** {@inheritDoc} */
 280     @Override protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
 281         final Slider s = getSkinnable();
 282         if (s.getOrientation() == Orientation.HORIZONTAL) {
 283             if(showTickMarks) {
 284                 return Math.max(140, tickLine.prefWidth(-1));
 285             } else {
 286                 return 140;
 287             }
 288         } else {
 289             double axisWidth = showTickMarks ? (tickLine.prefWidth(-1) + trackToTickGap) : 0;
 290             return leftInset + Math.max(thumb.prefWidth(-1), track.prefWidth(-1)) + axisWidth + rightInset;
 291         }
 292     }
 293 
 294     /** {@inheritDoc} */
 295     @Override protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
 296         final Slider s = getSkinnable();
 297         if (s.getOrientation() == Orientation.HORIZONTAL) {
 298             return topInset + Math.max(thumb.prefHeight(-1), track.prefHeight(-1)) +
 299                     ((showTickMarks) ? (trackToTickGap+tickLine.prefHeight(-1)) : 0)  + bottomInset;
 300         } else {
 301             if(showTickMarks) {
 302                 return Math.max(140, tickLine.prefHeight(-1));
 303             } else {
 304                 return 140;
 305             }
 306         }
 307     }
 308 
 309     /** {@inheritDoc} */
 310     @Override protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
 311         if (getSkinnable().getOrientation() == Orientation.HORIZONTAL) {
 312             return Double.MAX_VALUE;
 313         } else {
 314             return getSkinnable().prefWidth(-1);
 315         }
 316     }
 317 
 318     /** {@inheritDoc} */
 319     @Override protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
 320         if (getSkinnable().getOrientation() == Orientation.HORIZONTAL) {
 321             return getSkinnable().prefHeight(width);
 322         } else {
 323             return Double.MAX_VALUE;
 324         }
 325     }
 326 
 327 
 328 
 329     /***************************************************************************
 330      *                                                                         *
 331      * Private implementation                                                  *
 332      *                                                                         *
 333      **************************************************************************/
 334 
 335     private void initialize() {
 336         thumb = new StackPane() {
 337             @Override
 338             public Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) {
 339                 switch (attribute) {
 340                     case VALUE: return getSkinnable().getValue();
 341                     default: return super.queryAccessibleAttribute(attribute, parameters);
 342                 }
 343             }
 344         };
 345         thumb.getStyleClass().setAll("thumb");
 346         thumb.setAccessibleRole(AccessibleRole.THUMB);
 347         track = new StackPane();
 348         track.getStyleClass().setAll("track");
 349 //        horizontal = getSkinnable().isVertical();
 350 
 351         getChildren().clear();
 352         getChildren().addAll(track, thumb);
 353         setShowTickMarks(getSkinnable().isShowTickMarks(), getSkinnable().isShowTickLabels());
 354         track.setOnMousePressed(me -> {
 355             if (!thumb.isPressed()) {
 356                 trackClicked = true;
 357                 if (getSkinnable().getOrientation() == Orientation.HORIZONTAL) {
 358                     behavior.trackPress(me, (me.getX() / trackLength));
 359                 } else {
 360                     behavior.trackPress(me, (me.getY() / trackLength));
 361                 }
 362                 trackClicked = false;
 363             }
 364         });
 365         
 366         track.setOnMouseDragged(me -> {
 367             if (!thumb.isPressed()) {
 368                 if (getSkinnable().getOrientation() == Orientation.HORIZONTAL) {
 369                     behavior.trackPress(me, (me.getX() / trackLength));
 370                 } else {
 371                     behavior.trackPress(me, (me.getY() / trackLength));
 372                 }
 373             }
 374         });
 375 
 376         thumb.setOnMousePressed(me -> {
 377             behavior.thumbPressed(me, 0.0f);
 378             dragStart = thumb.localToParent(me.getX(), me.getY());
 379             preDragThumbPos = (getSkinnable().getValue() - getSkinnable().getMin()) /
 380                     (getSkinnable().getMax() - getSkinnable().getMin());
 381         });
 382 
 383         thumb.setOnMouseReleased(me -> {
 384             behavior.thumbReleased(me);
 385         });
 386 
 387         thumb.setOnMouseDragged(me -> {
 388             Point2D cur = thumb.localToParent(me.getX(), me.getY());
 389             double dragPos = (getSkinnable().getOrientation() == Orientation.HORIZONTAL) ?
 390                     cur.getX() - dragStart.getX() : -(cur.getY() - dragStart.getY());
 391             behavior.thumbDragged(me, preDragThumbPos + dragPos / trackLength);
 392         });
 393     }
 394 
 395     private void setShowTickMarks(boolean ticksVisible, boolean labelsVisible) {
 396         showTickMarks = (ticksVisible || labelsVisible);
 397         Slider slider = getSkinnable();
 398         if (showTickMarks) {
 399             if (tickLine == null) {
 400                 tickLine = new NumberAxis();
 401                 tickLine.setAutoRanging(false);
 402                 tickLine.setSide(slider.getOrientation() == Orientation.VERTICAL ? Side.RIGHT : (slider.getOrientation() == null) ? Side.RIGHT: Side.BOTTOM);
 403                 tickLine.setUpperBound(slider.getMax());
 404                 tickLine.setLowerBound(slider.getMin());
 405                 tickLine.setTickUnit(slider.getMajorTickUnit());
 406                 tickLine.setTickMarkVisible(ticksVisible);
 407                 tickLine.setTickLabelsVisible(labelsVisible);
 408                 tickLine.setMinorTickVisible(ticksVisible);
 409                 // add 1 to the slider minor tick count since the axis draws one
 410                 // less minor ticks than the number given.
 411                 tickLine.setMinorTickCount(Math.max(slider.getMinorTickCount(),0) + 1);
 412                 if (slider.getLabelFormatter() != null) {
 413                     tickLine.setTickLabelFormatter(stringConverterWrapper);
 414                 }
 415                 getChildren().clear();
 416                 getChildren().addAll(tickLine, track, thumb);
 417             } else {
 418                 tickLine.setTickLabelsVisible(labelsVisible);
 419                 tickLine.setTickMarkVisible(ticksVisible);
 420                 tickLine.setMinorTickVisible(ticksVisible);
 421             }
 422         } 
 423         else  {
 424             getChildren().clear();
 425             getChildren().addAll(track, thumb);
 426 //            tickLine = null;
 427         }
 428 
 429         getSkinnable().requestLayout();
 430     }
 431 
 432     /**
 433      * Called when ever either min, max or value changes, so thumb's layoutX, Y is recomputed.
 434      */
 435     void positionThumb(final boolean animate) {
 436         Slider s = getSkinnable();
 437         if (s.getValue() > s.getMax()) return;// this can happen if we are bound to something 
 438         boolean horizontal = s.getOrientation() == Orientation.HORIZONTAL;
 439         final double endX = (horizontal) ? trackStart + (((trackLength * ((s.getValue() - s.getMin()) /
 440                 (s.getMax() - s.getMin()))) - thumbWidth/2)) : thumbLeft;
 441         final double endY = (horizontal) ? thumbTop :
 442             snappedTopInset() + trackLength - (trackLength * ((s.getValue() - s.getMin()) /
 443                 (s.getMax() - s.getMin()))); //  - thumbHeight/2
 444         
 445         if (animate) {
 446             // lets animate the thumb transition
 447             final double startX = thumb.getLayoutX();
 448             final double startY = thumb.getLayoutY();
 449             Transition transition = new Transition() {
 450                 {
 451                     setCycleDuration(Duration.millis(200));
 452                 }
 453 
 454                 @Override protected void interpolate(double frac) {
 455                     if (!Double.isNaN(startX)) {
 456                         thumb.setLayoutX(startX + frac * (endX - startX));
 457                     }
 458                     if (!Double.isNaN(startY)) {
 459                         thumb.setLayoutY(startY + frac * (endY - startY));
 460                     }
 461                 }
 462             };
 463             transition.play();
 464         } else {
 465             thumb.setLayoutX(endX);
 466             thumb.setLayoutY(endY);
 467         }
 468     }
 469 
 470     double minTrackLength() {
 471         return 2*thumb.prefWidth(-1);
 472     }
 473 }
 474