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