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