/* * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javafx.scene.control; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javafx.beans.property.BooleanProperty; import javafx.beans.property.DoubleProperty; import javafx.beans.property.DoublePropertyBase; import javafx.beans.property.IntegerProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.WritableValue; import javafx.geometry.Orientation; import javafx.scene.AccessibleAction; import javafx.scene.AccessibleAttribute; import javafx.scene.AccessibleRole; import javafx.util.StringConverter; import com.sun.javafx.util.Utils; import javafx.css.CssMetaData; import javafx.css.PseudoClass; import javafx.css.StyleableBooleanProperty; import javafx.css.StyleableDoubleProperty; import javafx.css.StyleableIntegerProperty; import javafx.css.StyleableObjectProperty; import javafx.css.converter.BooleanConverter; import javafx.css.converter.EnumConverter; import javafx.css.converter.SizeConverter; import javafx.scene.control.skin.SliderSkin; import javafx.css.Styleable; import javafx.css.StyleableProperty; /** * The Slider Control is used to display a continuous or discrete range of * valid numeric choices and allows the user to interact with the control. It is * typically represented visually as having a "track" and a "knob" or "thumb" * which is dragged within the track. The Slider can optionally show tick marks * and labels indicating the different slider position values. *
* The three fundamental variables of the slider are min
,
* max
, and value
. The value
should always
* be a number within the range defined by min
and
* max
. min
should always be less than or equal to
* max
(although a slider who's min
and
* max
are equal is a degenerate case that makes no sense).
* min
defaults to 0, whereas max
defaults to 100.
*
* This first example creates a slider who's range, or span, goes from 0 to 1, * and who's value defaults to .5: * *
* import javafx.scene.control.Slider; * * Slider slider = new Slider(0, 1, 0.5); ** *
* This next example shows a slider with customized tick marks and tick mark * labels, which also spans from 0 to 1: * *
* import javafx.scene.control.Slider; * * Slider slider = new Slider(0, 1, 0.5); * slider.setShowTickMarks(true); * slider.setShowTickLabels(true); * slider.setMajorTickUnit(0.25f); * slider.setBlockIncrement(0.1f); ** @since JavaFX 2.0 */ public class Slider extends Control { /** * Creates a default Slider instance. */ public Slider() { initialize(); } /** * Constructs a Slider control with the specified slider min, max and current value values. * @param min Slider minimum value * @param max Slider maximum value * @param value Slider current value */ public Slider(double min, double max, double value) { setMax(max); setMin(min); setValue(value); adjustValues(); initialize(); } private void initialize() { //Initialize the style class to be 'slider'. getStyleClass().setAll(DEFAULT_STYLE_CLASS); setAccessibleRole(AccessibleRole.SLIDER); } /** * The maximum value represented by this Slider. This must be a * value greater than {@link #minProperty() min}. */ private DoubleProperty max; public final void setMax(double value) { maxProperty().set(value); } public final double getMax() { return max == null ? 100 : max.get(); } public final DoubleProperty maxProperty() { if (max == null) { max = new DoublePropertyBase(100) { @Override protected void invalidated() { if (get() < getMin()) { setMin(get()); } adjustValues(); notifyAccessibleAttributeChanged(AccessibleAttribute.MAX_VALUE); } @Override public Object getBean() { return Slider.this; } @Override public String getName() { return "max"; } }; } return max; } /** * The minimum value represented by this Slider. This must be a * value less than {@link #maxProperty() max}. */ private DoubleProperty min; public final void setMin(double value) { minProperty().set(value); } public final double getMin() { return min == null ? 0 : min.get(); } public final DoubleProperty minProperty() { if (min == null) { min = new DoublePropertyBase(0) { @Override protected void invalidated() { if (get() > getMax()) { setMax(get()); } adjustValues(); notifyAccessibleAttributeChanged(AccessibleAttribute.MIN_VALUE); } @Override public Object getBean() { return Slider.this; } @Override public String getName() { return "min"; } }; } return min; } /** * The current value represented by this Slider. This value must * always be between {@link #minProperty() min} and {@link #maxProperty() max}, * inclusive. If it is ever out of bounds either due to {@code min} or * {@code max} changing or due to itself being changed, then it will * be clamped to always remain valid. */ private DoubleProperty value; public final void setValue(double value) { if (!valueProperty().isBound()) valueProperty().set(value); } public final double getValue() { return value == null ? 0 : value.get(); } public final DoubleProperty valueProperty() { if (value == null) { value = new DoublePropertyBase(0) { @Override protected void invalidated() { adjustValues(); notifyAccessibleAttributeChanged(AccessibleAttribute.VALUE); } @Override public Object getBean() { return Slider.this; } @Override public String getName() { return "value"; } }; } return value; } /** * When true, indicates the current value of this Slider is changing. * It provides notification that the value is changing. Once the value is * computed, it is reset back to false. */ private BooleanProperty valueChanging; public final void setValueChanging(boolean value) { valueChangingProperty().set(value); } public final boolean isValueChanging() { return valueChanging == null ? false : valueChanging.get(); } public final BooleanProperty valueChangingProperty() { if (valueChanging == null) { valueChanging = new SimpleBooleanProperty(this, "valueChanging", false); } return valueChanging; } // /** // * The {@code span} is the distance, or quantity, between min and max value. // * This will be strictly non-negative, since both {@code min} and // * {@code max} are forced to maintain a proper relationship. // */ // // public def span = bind max - min; /** * The orientation of the {@code Slider} can either be horizontal * or vertical. */ private ObjectProperty
* This value should be positive and should be a value less than the
* span. Out of range values are essentially the same as disabling
* tick marks.
*/
private DoubleProperty majorTickUnit;
public final void setMajorTickUnit(double value) {
if (value <= 0) {
throw new IllegalArgumentException("MajorTickUnit cannot be less than or equal to 0.");
}
majorTickUnitProperty().set(value);
}
public final double getMajorTickUnit() {
return majorTickUnit == null ? 25 : majorTickUnit.get();
}
public final DoubleProperty majorTickUnitProperty() {
if (majorTickUnit == null) {
majorTickUnit = new StyleableDoubleProperty(25) {
@Override
public void invalidated() {
if (get() <= 0) {
throw new IllegalArgumentException("MajorTickUnit cannot be less than or equal to 0.");
}
}
@Override
public CssMetaDatanewValue
. The
* value
is the actual amount between the
* {@link #minProperty() min} and {@link #maxProperty() max}. This function
* also takes into account {@link #snapToTicksProperty() snapToTicks}, which
* is the main difference between adjustValue and setValue. It also ensures
* that the value is some valid number between min and max.
*
* @expert This function is intended to be used by experts, primarily
* by those implementing new Skins or Behaviors. It is not common
* for developers or designers to access this function directly.
*/
public void adjustValue(double newValue) {
// figure out the "value" associated with the specified position
final double _min = getMin();
final double _max = getMax();
if (_max <= _min) return;
newValue = newValue < _min ? _min : newValue;
newValue = newValue > _max ? _max : newValue;
setValue(snapValueToTicks(newValue));
}
/**
* Increments the value by {@link #blockIncrementProperty() blockIncrement}, bounded by max. If the
* max is less than or equal to the min, then this method does nothing.
*/
public void increment() {
adjustValue(getValue() + getBlockIncrement());
}
/**
* Decrements the value by {@link #blockIncrementProperty() blockIncrement}, bounded by max. If the
* max is less than or equal to the min, then this method does nothing.
*/
public void decrement() {
adjustValue(getValue() - getBlockIncrement());
}
/**
* Ensures that min is always < max, that value is always
* somewhere between the two, and that if snapToTicks is set then the
* value will always be set to align with a tick mark.
*/
private void adjustValues() {
if ((getValue() < getMin() || getValue() > getMax()) /* && !isReadOnly(value)*/)
setValue(Utils.clamp(getMin(), getValue(), getMax()));
}
/**
* Utility function which, given the specified value, will position it
* either aligned with a tick, or simply clamp between min & max value,
* depending on whether snapToTicks is set.
*
* @expert This function is intended to be used by experts, primarily
* by those implementing new Skins or Behaviors. It is not common
* for developers or designers to access this function directly.
*/
private double snapValueToTicks(double val) {
double v = val;
if (isSnapToTicks()) {
double tickSpacing = 0;
// compute the nearest tick to this value
if (getMinorTickCount() != 0) {
tickSpacing = getMajorTickUnit() / (Math.max(getMinorTickCount(),0)+1);
} else {
tickSpacing = getMajorTickUnit();
}
int prevTick = (int)((v - getMin())/ tickSpacing);
double prevTickValue = (prevTick) * tickSpacing + getMin();
double nextTickValue = (prevTick + 1) * tickSpacing + getMin();
v = Utils.nearest(prevTickValue, v, nextTickValue);
}
return Utils.clamp(getMin(), v, getMax());
}
/** {@inheritDoc} */
@Override protected Skin> createDefaultSkin() {
return new SliderSkin(this);
}
/***************************************************************************
* *
* Stylesheet Handling *
* *
**************************************************************************/
private static final String DEFAULT_STYLE_CLASS = "slider";
private static class StyleableProperties {
private static final CssMetaData