com.speedment.tool.propertyeditor.item.DefaultSpinnerItem Maven / Gradle / Ivy
/**
*
* Copyright (c) 2006-2016, Speedment, Inc. All Rights Reserved.
*
* 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 com.speedment.tool.propertyeditor.item;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ObservableIntegerValue;
import javafx.scene.Node;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Spinner;
import javafx.scene.control.SpinnerValueFactory;
import javafx.scene.control.SpinnerValueFactory.IntegerSpinnerValueFactory;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import java.util.function.UnaryOperator;
import static java.util.Objects.requireNonNull;
/**
* An editor for editing a StringProperty via an IntegerSpinner, which has a default value. The user
* may opt to use the default value or not, by checking or un-checking a CheckBox. The property
* will bind to Spinner.valueProperty()
*
* @author Simon Jonasson
* @since 3.0.0
*/
public class DefaultSpinnerItem extends AbstractLabelTooltipItem {
private final ObservableIntegerValue defaultValue;
private final ObjectProperty value; //Output value
private final ObjectProperty customValue;
private final int min, max;
/**
* Creates a new DefaultSpinnerItem.
*
* While the CheckBox is checked, the Spinner will be disabled,
* and the property will always have the default value.
* While the CheckBox is un-checked, the Spinner will be enabled,
* and the property will always have the Spinner's current value.
*
* Upon construction, the editor decides whether to check the default box
* by comparing the property value to the default value. If they match, or
* the property value is {@code null}, the CheckBox will be checked.
*
* @param label the label text
* @param defaultValue the default value
* @param value the property to be edited
* @param tooltip the tooltip
*/
public DefaultSpinnerItem(
String label,
ObservableIntegerValue defaultValue,
IntegerProperty value,
String tooltip) {
this(label, defaultValue, value, tooltip, Integer.MIN_VALUE, Integer.MAX_VALUE);
}
/**
* Creates a new DefaultSpinnerItem.
*
* While the CheckBox is checked, the Spinner will be disabled,
* and the property will always have the default value.
* While the CheckBox is un-checked, the Spinner will be enabled,
* and the property will always have the Spinner's current value.
*
* Upon construction, the editor decides whether to check the default box
* by comparing the property value to the default value. If they match, or
* the property value is {@code null}, the CheckBox will be checked.
*
* @param label the label text
* @param defaultValue the default value
* @param value the property to be edited
* @param tooltip the tooltip
* @param decorator the editor decorator
*/
public DefaultSpinnerItem(
String label,
ObservableIntegerValue defaultValue,
IntegerProperty value,
String tooltip,
UnaryOperator decorator) {
this(label, defaultValue, value, tooltip, Integer.MIN_VALUE, Integer.MAX_VALUE, decorator);
}
/**
* Creates a new DefaultSpinnerItem.
*
* While the CheckBox is checked, the Spinner will be disabled,
* and the property will always have the default value.
* While the CheckBox is un-checked, the Spinner will be enabled,
* and the property will always have the Spinner's current value.
*
* Upon construction, the editor decides whether to check the default box
* by comparing the property value to the default value. If they match, or
* the property value is {@code null}, the CheckBox will be checked.
*
* @param label the label text
* @param defaultValue the default value
* @param value the property to be edited
* @param tooltip the tooltip
* @param min the minimum spinner value
* @param max the maximum spinner value
*/
public DefaultSpinnerItem(
String label,
ObservableIntegerValue defaultValue,
IntegerProperty value,
String tooltip,
int min,
int max) {
this(label, defaultValue, value, tooltip, min, max, NO_DECORATOR);
}
/**
* Creates a new DefaultSpinnerItem.
*
* While the CheckBox is checked, the Spinner will be disabled,
* and the property will always have the default value.
* While the CheckBox is un-checked, the Spinner will be enabled,
* and the property will always have the Spinner's current value.
*
* Upon construction, the editor decides whether to check the default box
* by comparing the property value to the default value. If they match, or
* the property value is {@code null}, the CheckBox will be checked.
*
* @param label the label text
* @param defaultValue the default value
* @param value the property to be edited
* @param tooltip the tooltip
* @param min the minimum spinner value
* @param max the maximum spinner value
* @param decorator the editor decorator
*/
public DefaultSpinnerItem(
String label,
ObservableIntegerValue defaultValue,
IntegerProperty value,
String tooltip,
int min,
int max,
UnaryOperator decorator) {
super(label, tooltip, decorator);
this.defaultValue = requireNonNull(defaultValue);
this.value = requireNonNull(value).asObject();
this.customValue = new SimpleIntegerProperty().asObject();
this.min = min;
this.max = max;
}
@Override
protected Node createUndecoratedEditor() {
final boolean usesDefaultValue = value.getValue() == null || value.getValue().equals(defaultValue.getValue());
final HBox container = new HBox();
final CheckBox auto = new CheckBox("Auto");
final Spinner spinner = new Spinner<>();
final IntegerSpinnerValueFactory svf = new SpinnerValueFactory.IntegerSpinnerValueFactory(min, max);
spinner.setValueFactory(svf);
spinner.disableProperty().bind(auto.selectedProperty());
spinner.setEditable(true);
auto.setSelected(usesDefaultValue);
attachListener(auto.selectedProperty(), (ov, o, isAuto) ->
setSpinnerBehaviour(svf, isAuto, defaultValue, customValue)
);
customValue.setValue(usesDefaultValue ? defaultValue.get() : value.get());
setSpinnerBehaviour(svf, usesDefaultValue, defaultValue, customValue);
final TextField editor = spinner.getEditor();
attachListener(editor.textProperty(), (ov, oldVal, newVal) -> {
try {
Integer.parseInt(newVal);
} catch (final NumberFormatException ex){
editor.setText(oldVal);
}
});
attachListener(editor.focusedProperty(), (ov, wasFocused, isFocused) -> {
if(wasFocused) {
try{
final int editorValue = Integer.parseInt( editor.getText() );
if( editorValue > max){
editor.setText( String.valueOf(max) );
} else if( editorValue < min ){
editor.setText( String.valueOf(min) );
}
} catch (final NumberFormatException ex) {
throw new RuntimeException("Unable to parse an integer from editor field", ex);
}
}
});
attachListener(svf.valueProperty(), (ov, o, n) -> {
if( n == null || n == defaultValue.get() ){
value.setValue(null);
} else {
value.setValue(n);
}
});
container.getChildren().addAll(auto, spinner);
return container;
}
private static void setSpinnerBehaviour(
IntegerSpinnerValueFactory svf,
boolean useDefaultValue,
ObservableIntegerValue defaultValue,
ObjectProperty customValue) {
if (useDefaultValue) {
svf.valueProperty().unbindBidirectional( customValue );
svf.setValue(defaultValue.get());
} else {
svf.setValue(customValue.getValue());
svf.valueProperty().bindBidirectional( customValue );
}
}
}