1 /* 2 * Copyright (c) 2010, 2015, 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 com.sun.javafx.util.Utils; 29 import com.sun.javafx.css.converters.EnumConverter; 30 import com.sun.javafx.css.converters.SizeConverter; 31 import com.sun.javafx.scene.control.skin.ScrollBarSkin; 32 33 import javafx.beans.property.DoubleProperty; 34 import javafx.beans.property.ObjectProperty; 35 import javafx.beans.property.SimpleDoubleProperty; 36 import javafx.beans.value.WritableValue; 37 import javafx.css.CssMetaData; 38 import javafx.css.PseudoClass; 39 import javafx.css.Styleable; 40 import javafx.css.StyleableDoubleProperty; 41 import javafx.css.StyleableObjectProperty; 42 import javafx.css.StyleableProperty; 43 import javafx.geometry.Orientation; 44 import javafx.scene.AccessibleAction; 45 import javafx.scene.AccessibleAttribute; 46 import javafx.scene.AccessibleRole; 47 48 import java.util.ArrayList; 49 import java.util.Collections; 50 import java.util.List; 51 52 53 /** 54 * Either a horizontal or vertical bar with increment and decrement buttons and 55 * a "thumb" with which the user can interact. Typically not used alone but used 56 * for building up more complicated controls such as the ScrollPane and ListView. 57 * <p> 58 * ScrollBar sets focusTraversable to false. 59 * </p> 60 * 61 * <p> 62 * This example creates a vertical ScrollBar : 63 * <pre><code> 64 * import javafx.scene.control.ScrollBar; 65 * 66 * ScrollBar s1 = new ScrollBar(); 67 * s1.setOrientation(Orientation.VERTICAL); 68 * </code></pre> 69 * 70 * Implementation of ScrollBar According to JavaFX UI Control API Specification 71 * @since JavaFX 2.0 72 */ 73 74 public class ScrollBar extends Control { 75 76 /*************************************************************************** 77 * * 78 * Constructors * 79 * * 80 **************************************************************************/ 81 82 /** 83 * Creates a new horizontal ScrollBar (ie getOrientation() == Orientation.HORIZONTAL). 84 * 85 * 86 */ 87 public ScrollBar() { 88 // TODO : we need to ensure we have a width and height 89 setWidth(ScrollBarSkin.DEFAULT_WIDTH); 90 setHeight(ScrollBarSkin.DEFAULT_LENGTH); 91 getStyleClass().setAll(DEFAULT_STYLE_CLASS); 92 setAccessibleRole(AccessibleRole.SCROLL_BAR); 93 // focusTraversable is styleable through css. Calling setFocusTraversable 94 // makes it look to css like the user set the value and css will not 95 // override. Initializing focusTraversable by calling applyStyle with null 96 // for StyleOrigin ensures that css will be able to override the value. 97 ((StyleableProperty<Boolean>)(WritableValue<Boolean>)focusTraversableProperty()).applyStyle(null,Boolean.FALSE); 98 99 // set pseudo-class state to horizontal 100 pseudoClassStateChanged(HORIZONTAL_PSEUDOCLASS_STATE, true); 101 102 } 103 /*************************************************************************** 104 * * 105 * Properties * 106 * * 107 **************************************************************************/ 108 /** 109 * The minimum value represented by this {@code ScrollBar}. This should be a 110 * value less than or equal to {@link #maxProperty max}. Default value is 0. 111 */ 112 private DoubleProperty min; 113 public final void setMin(double value) { 114 minProperty().set(value); 115 } 116 117 public final double getMin() { 118 return min == null ? 0 : min.get(); 119 } 120 121 public final DoubleProperty minProperty() { 122 if (min == null) { 123 min = new SimpleDoubleProperty(this, "min"); 124 } 125 return min; 126 } 127 /** 128 * The maximum value represented by this {@code ScrollBar}. This should be a 129 * value greater than or equal to {@link #minProperty min}. Default value is 100. 130 */ 131 private DoubleProperty max; 132 public final void setMax(double value) { 133 maxProperty().set(value); 134 } 135 136 public final double getMax() { 137 return max == null ? 100 : max.get(); 138 } 139 140 public final DoubleProperty maxProperty() { 141 if (max == null) { 142 max = new SimpleDoubleProperty(this, "max", 100); 143 } 144 return max; 145 } 146 /** 147 * The current value represented by this {@code ScrollBar}. This value should 148 * be between {@link #minProperty min} and {@link #maxProperty max}, inclusive. 149 */ 150 private DoubleProperty value; 151 public final void setValue(double value) { 152 valueProperty().set(value); 153 } 154 155 public final double getValue() { 156 return value == null ? 0 : value.get(); 157 } 158 159 public final DoubleProperty valueProperty() { 160 if (value == null) { 161 value = new SimpleDoubleProperty(this, "value"); 162 } 163 return value; 164 } 165 /** 166 * The orientation of the {@code ScrollBar} can either be {@link javafx.geometry.Orientation#HORIZONTAL HORIZONTAL} 167 * or {@link javafx.geometry.Orientation#VERTICAL VERTICAL}. 168 */ 169 private ObjectProperty<Orientation> orientation; 170 public final void setOrientation(Orientation value) { 171 orientationProperty().set(value); 172 } 173 174 public final Orientation getOrientation() { 175 return orientation == null ? Orientation.HORIZONTAL : orientation.get(); 176 } 177 178 public final ObjectProperty<Orientation> orientationProperty() { 179 if (orientation == null) { 180 orientation = new StyleableObjectProperty<Orientation>(Orientation.HORIZONTAL) { 181 @Override protected void invalidated() { 182 final boolean vertical = (get() == Orientation.VERTICAL); 183 pseudoClassStateChanged(VERTICAL_PSEUDOCLASS_STATE, vertical); 184 pseudoClassStateChanged(HORIZONTAL_PSEUDOCLASS_STATE, !vertical); 185 } 186 187 @Override 188 public CssMetaData<ScrollBar,Orientation> getCssMetaData() { 189 return StyleableProperties.ORIENTATION; 190 } 191 192 @Override 193 public Object getBean() { 194 return ScrollBar.this; 195 } 196 197 @Override 198 public String getName() { 199 return "orientation"; 200 } 201 }; 202 } 203 return orientation; 204 } 205 206 /** 207 * The amount by which to adjust the ScrollBar when the {@link #increment() increment} or 208 * {@link #decrement() decrement} methods are called. 209 */ 210 private DoubleProperty unitIncrement; 211 public final void setUnitIncrement(double value) { 212 unitIncrementProperty().set(value); 213 } 214 215 public final double getUnitIncrement() { 216 return unitIncrement == null ? 1 : unitIncrement.get(); 217 } 218 219 public final DoubleProperty unitIncrementProperty() { 220 if (unitIncrement == null) { 221 unitIncrement = new StyleableDoubleProperty(1) { 222 223 @Override 224 public CssMetaData<ScrollBar,Number> getCssMetaData() { 225 return StyleableProperties.UNIT_INCREMENT; 226 } 227 228 @Override 229 public Object getBean() { 230 return ScrollBar.this; 231 } 232 233 @Override 234 public String getName() { 235 return "unitIncrement"; 236 } 237 }; 238 } 239 return unitIncrement; 240 } 241 /** 242 * The amount by which to adjust the scrollbar if the track of the bar is 243 * clicked. 244 */ 245 private DoubleProperty blockIncrement; 246 public final void setBlockIncrement(double value) { 247 blockIncrementProperty().set(value); 248 } 249 250 public final double getBlockIncrement() { 251 return blockIncrement == null ? 10 : blockIncrement.get(); 252 } 253 254 public final DoubleProperty blockIncrementProperty() { 255 if (blockIncrement == null) { 256 blockIncrement = new StyleableDoubleProperty(10) { 257 258 @Override 259 public CssMetaData<ScrollBar,Number> getCssMetaData() { 260 return StyleableProperties.BLOCK_INCREMENT; 261 } 262 263 @Override 264 public Object getBean() { 265 return ScrollBar.this; 266 } 267 268 @Override 269 public String getName() { 270 return "blockIncrement"; 271 } 272 }; 273 } 274 return blockIncrement; 275 } 276 /** 277 * Visible amount of the scrollbar's range, typically represented by 278 * the size of the scroll bar's thumb. 279 */ 280 private DoubleProperty visibleAmount; 281 282 public final void setVisibleAmount(double value) { 283 visibleAmountProperty().set(value); 284 } 285 286 public final double getVisibleAmount() { 287 return visibleAmount == null ? 15 : visibleAmount.get(); 288 } 289 290 public final DoubleProperty visibleAmountProperty() { 291 if (visibleAmount == null) { 292 visibleAmount = new SimpleDoubleProperty(this, "visibleAmount"); 293 } 294 return visibleAmount; 295 } 296 297 /*************************************************************************** 298 * * 299 * Methods * 300 * * 301 **************************************************************************/ 302 303 /** 304 * Adjusts the {@link #valueProperty() value} property by 305 * {@link #blockIncrementProperty() blockIncrement}. The {@code position} is the fractional amount 306 * between the {@link #minProperty min} and {@link #maxProperty max}. For 307 * example, it might be 50%. If {@code #minProperty min} were 0 and {@code #maxProperty max} 308 * were 100 and {@link #valueProperty() value} were 25, then a position of .5 would indicate 309 * that we should increment {@link #valueProperty() value} by 310 * {@code blockIncrement}. If {@link #valueProperty() value} were 75, then a 311 * position of .5 would indicate that we 312 * should decrement {@link #valueProperty() value} by {@link #blockIncrementProperty blockIncrement}. 313 * 314 * @expert This function is intended to be used by experts, primarily 315 * by those implementing new Skins or Behaviors. It is not common 316 * for developers or designers to access this function directly. 317 */ 318 public void adjustValue(double position) { 319 // figure out the "value" associated with the specified position 320 double posValue = ((getMax() - getMin()) * Utils.clamp(0, position, 1))+getMin(); 321 double newValue; 322 if (Double.compare(posValue, getValue()) != 0) { 323 if (posValue > getValue()) { 324 newValue = getValue() + getBlockIncrement(); 325 } 326 else { 327 newValue = getValue() - getBlockIncrement(); 328 } 329 330 boolean incrementing = position > ((getValue() - getMin())/(getMax() - getMin())); 331 if (incrementing && newValue > posValue) newValue = posValue; 332 if (! incrementing && newValue < posValue) newValue = posValue; 333 setValue(Utils.clamp(getMin(), newValue, getMax())); 334 } 335 } 336 337 /** 338 * Increments the value of the {@code ScrollBar} by the 339 * {@link #unitIncrementProperty unitIncrement} 340 */ 341 public void increment() { 342 setValue(Utils.clamp(getMin(), getValue() + getUnitIncrement(), getMax())); 343 } 344 345 /** 346 * Decrements the value of the {@code ScrollBar} by the 347 * {@link #unitIncrementProperty unitIncrement} 348 */ 349 public void decrement() { 350 setValue(Utils.clamp(getMin(), getValue() - getUnitIncrement(), getMax())); 351 } 352 353 private void blockIncrement() { 354 adjustValue(getValue() + getBlockIncrement()); 355 } 356 357 private void blockDecrement() { 358 adjustValue(getValue() - getBlockIncrement()); 359 } 360 361 /** {@inheritDoc} */ 362 @Override protected Skin<?> createDefaultSkin() { 363 return new ScrollBarSkin(this); 364 } 365 366 /*************************************************************************** 367 * * 368 * Stylesheet Handling * 369 * * 370 **************************************************************************/ 371 372 /** 373 * Initialize the style class to 'scroll-bar'. 374 * 375 * This is the selector class from which CSS can be used to style 376 * this control. 377 */ 378 private static final String DEFAULT_STYLE_CLASS = "scroll-bar"; 379 380 private static class StyleableProperties { 381 private static final CssMetaData<ScrollBar,Orientation> ORIENTATION = 382 new CssMetaData<ScrollBar,Orientation>("-fx-orientation", 383 new EnumConverter<Orientation>(Orientation.class), 384 Orientation.HORIZONTAL) { 385 386 @Override 387 public Orientation getInitialValue(ScrollBar node) { 388 // A vertical ScrollBar should remain vertical 389 return node.getOrientation(); 390 } 391 392 @Override 393 public boolean isSettable(ScrollBar n) { 394 return n.orientation == null || !n.orientation.isBound(); 395 } 396 397 @Override 398 public StyleableProperty<Orientation> getStyleableProperty(ScrollBar n) { 399 return (StyleableProperty<Orientation>)(WritableValue<Orientation>)n.orientationProperty(); 400 } 401 }; 402 403 private static final CssMetaData<ScrollBar,Number> UNIT_INCREMENT = 404 new CssMetaData<ScrollBar,Number>("-fx-unit-increment", 405 SizeConverter.getInstance(), 1.0) { 406 407 @Override 408 public boolean isSettable(ScrollBar n) { 409 return n.unitIncrement == null || !n.unitIncrement.isBound(); 410 } 411 412 @Override 413 public StyleableProperty<Number> getStyleableProperty(ScrollBar n) { 414 return (StyleableProperty<Number>)(WritableValue<Number>)n.unitIncrementProperty(); 415 } 416 417 }; 418 419 private static final CssMetaData<ScrollBar,Number> BLOCK_INCREMENT = 420 new CssMetaData<ScrollBar,Number>("-fx-block-increment", 421 SizeConverter.getInstance(), 10.0) { 422 423 @Override 424 public boolean isSettable(ScrollBar n) { 425 return n.blockIncrement == null || !n.blockIncrement.isBound(); 426 } 427 428 @Override 429 public StyleableProperty<Number> getStyleableProperty(ScrollBar n) { 430 return (StyleableProperty<Number>)(WritableValue<Number>)n.blockIncrementProperty(); 431 } 432 433 }; 434 435 private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES; 436 static { 437 final List<CssMetaData<? extends Styleable, ?>> styleables = 438 new ArrayList<CssMetaData<? extends Styleable, ?>>(Control.getClassCssMetaData()); 439 styleables.add(ORIENTATION); 440 styleables.add(UNIT_INCREMENT); 441 styleables.add(BLOCK_INCREMENT); 442 STYLEABLES = Collections.unmodifiableList(styleables); 443 } 444 } 445 446 /** 447 * @return The CssMetaData associated with this class, which may include the 448 * CssMetaData of its super classes. 449 * @since JavaFX 8.0 450 */ 451 public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() { 452 return StyleableProperties.STYLEABLES; 453 } 454 455 /** 456 * {@inheritDoc} 457 * @since JavaFX 8.0 458 */ 459 @Override 460 public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() { 461 return getClassCssMetaData(); 462 } 463 464 /** 465 * Pseud-class indicating this is a vertical ScrollBar. 466 */ 467 private static final PseudoClass VERTICAL_PSEUDOCLASS_STATE = 468 PseudoClass.getPseudoClass("vertical"); 469 470 /** 471 * Pseudo-class indicating this is a horizontal ScrollBar. 472 */ 473 private static final PseudoClass HORIZONTAL_PSEUDOCLASS_STATE = 474 PseudoClass.getPseudoClass("horizontal"); 475 476 /** 477 * Most Controls return true for focusTraversable, so Control overrides 478 * this method to return true, but ScrollBar returns false for 479 * focusTraversable's initial value; hence the override of the override. 480 * This method is called from CSS code to get the correct initial value. 481 * @treatAsPrivate implementation detail 482 */ 483 @Deprecated @Override 484 protected /*do not make final*/ Boolean impl_cssGetFocusTraversableInitialValue() { 485 return Boolean.FALSE; 486 } 487 488 489 490 /*************************************************************************** 491 * * 492 * Accessibility handling * 493 * * 494 **************************************************************************/ 495 496 @Override 497 public Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) { 498 switch (attribute) { 499 case VALUE: return getValue(); 500 case MAX_VALUE: return getMax(); 501 case MIN_VALUE: return getMin(); 502 case ORIENTATION: return getOrientation(); 503 default: return super.queryAccessibleAttribute(attribute, parameters); 504 } 505 } 506 507 @Override 508 public void executeAccessibleAction(AccessibleAction action, Object... parameters) { 509 switch (action) { 510 case INCREMENT: increment(); break; 511 case DECREMENT: decrement(); break; 512 case BLOCK_INCREMENT: blockIncrement(); break; 513 case BLOCK_DECREMENT: blockDecrement(); break; 514 case SET_VALUE: { 515 Double value = (Double) parameters[0]; 516 if (value != null) setValue(value); 517 break; 518 } 519 default: super.executeAccessibleAction(action, parameters); 520 } 521 } 522 }