All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sellcom.javafx.scene.control.DoubleSpinner Maven / Gradle / Ivy

/*
 * Copyright (c) 2015-2017 Petr Zelenka .
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.sellcom.javafx.scene.control;

import static org.sellcom.javafx.scene.input.KeyEvents.is;

import org.sellcom.core.Contract;
import org.sellcom.core.Strings;

import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.Spinner;
import javafx.scene.control.SpinnerValueFactory.DoubleSpinnerValueFactory;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;

/**
 * {@code Spinner} for {@code Double}s.
 *
 * @since 1.0
 *
 * @see Spinner
 */
public final class DoubleSpinner extends Spinner {

	private final double defaultValue;

	private final DoubleSpinnerValueFactory valueFactory;


	/**
	 * Creates a new spinner.
	 *
	 * @since 1.0
	 */
	public DoubleSpinner() {
		this(-Double.MAX_VALUE, Double.MAX_VALUE, 0.0, 1.0);
	}

	/**
	 * Creates a new spinner with the given minimum and maximum.
	 *
	 * @throws IllegalArgumentException if {@code max} < {@code min}
	 *
	 * @since 1.0
	 */
	public DoubleSpinner(double min, double max) {
		this(min, max, 0.0, 1.0);
	}

	/**
	 * Creates a new spinner with the given minimum, maximum, and initial value.
	 *
	 * @throws IllegalArgumentException if {@code max} < {@code min}
	 * @throws IllegalArgumentException if {@code initialValue} < {@code min}
	 * @throws IllegalArgumentException if {@code initialValue} > {@code max}
	 *
	 * @since 1.0
	 */
	public DoubleSpinner(double min, double max, double initialValue) {
		this(min, max, initialValue, 1.0);
	}

	/**
	 * Creates a new spinner with the given minimum, maximum, initial value, and step size.
	 *
	 * @throws IllegalArgumentException if {@code max} < {@code min}
	 * @throws IllegalArgumentException if {@code initialValue} < {@code min}
	 * @throws IllegalArgumentException if {@code initialValue} > {@code max}
	 * @throws IllegalArgumentException if {@code stepSize} ≤ 0
	 *
	 * @since 1.0
	 */
	public DoubleSpinner(double min, double max, double initialValue, double stepSize) {
		Contract.checkArgument(max >= min, "Maximum must be greater or equal to minimum: {0} < {1}", max, min);
		Contract.checkArgument(initialValue >= min, "Initial value must be greater or equal to minimum: {0} < {1}", initialValue, min);
		Contract.checkArgument(initialValue <= max, "Initial value must be less or equal to maximum: {0} > {1}", initialValue, max);
		Contract.checkArgument(stepSize > 0, "Step size must be positive: {0}", stepSize);

		defaultValue = initialValue;
		valueFactory = new DoubleSpinnerValueFactory(min, max, initialValue, stepSize);

		getEditor().focusedProperty().addListener(this::selectAllOnFocusGained);
		getEditor().setOnKeyPressed(this::handleKeyPressed);

		setEditable(true);
		setValueFactory(valueFactory);
	}


	/**
	 * Returns the maximum allowable value of this spinner.
	 *
	 * @since 1.0
	 */
	public double getMax() {
		return valueFactory.getMax();
	}

	/**
	 * Returns the minimum allowable value of this spinner.
	 *
	 * @since 1.0
	 */
	public double getMin() {
		return valueFactory.getMin();
	}

	/**
	 * Returns the step size of this spinner.
	 *
	 * @since 1.0
	 */
	public double getStepSize() {
		return valueFactory.getAmountToStepBy();
	}

	/**
	 * Checks whether the value of this spinner wraps around on reaching its allowable minimum or maximum value.
	 *
	 * @since 1.0
	 */
	public boolean isWrapAround() {
		return valueFactory.isWrapAround();
	}

	/**
	 * Returns the property containing the maximum allowable value of this spinner.
	 *
	 * @since 1.0
	 */
	public DoubleProperty maxProperty() {
		return valueFactory.maxProperty();
	}

	/**
	 * Returns the property containing the minimum allowable value of this spinner.
	 *
	 * @since 1.0
	 */
	public DoubleProperty minProperty() {
		return valueFactory.minProperty();
	}

	/**
	 * Sets the maximum allowable value of this spinner.
	 *
	 * @throws IllegalArgumentException if {@code max} < {@code min}
	 *
	 * @since 1.0
	 */
	public void setMax(double max) {
		Contract.checkArgument(max >= getMin(), "Maximum must be greater or equal to minimum: {0} < {1}", max, getMin());

		valueFactory.setMax(max);
	}

	/**
	 * Sets the minimum allowable value of this spinner.
	 *
	 * @throws IllegalArgumentException if {@code min} > {@code max}
	 *
	 * @since 1.0
	 */
	public void setMin(double min) {
		Contract.checkArgument(min <= getMax(), "Minimum must be less or equal to maximum: {0} > {1}", min, getMax());

		valueFactory.setMin(min);
	}

	/**
	 * Sets the step size of this spinner.
	 *
	 * @throws IllegalArgumentException if {@code stepSize} ≤ 0
	 *
	 * @since 1.0
	 */
	public void setStepSize(double stepSize) {
		Contract.checkArgument(stepSize > 0, "Step size must be positive: {0}", stepSize);

		valueFactory.setAmountToStepBy(stepSize);
	}

	/**
	 * Sets the value of this spinner.
	 *
	 * @throws IllegalArgumentException if {@code value} < {@code min}
	 * @throws IllegalArgumentException if {@code value} > {@code max}
	 *
	 * @since 1.0
	 */
	public void setValue(double value) {
		Contract.checkArgument(value >= getMin(), "Value must be greater or equal to minimum: {0} < {1}", value, getMin());
		Contract.checkArgument(value <= getMax(), "Value must be less or equal to maximum: {0} > {1}", value, getMax());

		valueFactory.setValue(value);
	}

	/**
	 * Sets whether the value of this spinner wraps around on reaching its allowable minimum or maximum value.
	 *
	 * @since 1.0
	 */
	public void setWrapAround(boolean wrapAround) {
		valueFactory.setWrapAround(wrapAround);
	}

	/**
	 * Returns the property containing the step size of this spinner.
	 *
	 * @since 1.0
	 */
	public DoubleProperty stepSizeProperty() {
		return valueFactory.amountToStepByProperty();
	}

	/**
	 * Returns the property containing whether the value of this spinner wraps around on reaching its allowable minimum or maximum value.
	 *
	 * @since 1.0
	 */
	public BooleanProperty wrapAroundProperty() {
		return valueFactory.wrapAroundProperty();
	}

	/**
	 * Returns the writable property containing the value of this spinner.
	 *
	 * @since 1.0
	 *
	 * @see #valueProperty()
	 */
	public final ObjectProperty writableValueProperty() {
		return valueFactory.valueProperty();
	}


	private void handleKeyPressed(KeyEvent event) {
		if (is(event, KeyCode.DOWN)) {
			event.consume();

			valueFactory.decrement(1);

			return;
		}
		if (is(event, KeyCode.UP)) {
			event.consume();

			valueFactory.increment(1);

			return;
		}

		TextField editor = getEditor();
		if (Strings.isNullOrEmpty(editor.getText())) {
			editor.setText(valueFactory.getConverter().toString(defaultValue));
			editor.selectAll();
		}
	}

	private void selectAllOnFocusGained(ObservableValue observable, Boolean oldValue, Boolean newValue) {
		if (newValue) {
			Platform.runLater(() -> getEditor().selectAll());
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy