FULL PRODUCT VERSION :
9.0.1, x86_64: "Java SE 9.0.1" /Library/Java/JavaVirtualMachines/jdk-9.0.1.jdk/Contents/Home
ADDITIONAL OS VERSION INFORMATION :
Darwin 16.7.0 Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64 x86_64
A DESCRIPTION OF THE PROBLEM :
The IntegerSpinner does not work as expected. The wrap around does not behave correctly.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the provided source code. Step through the spinner values by pressing the UP button. Note, when pressing the DOWN Button the behaviour is correct. I think the bug is in the following implementation:
static int wrapValue(int value, int min, int max) {
if (max == 0) {
throw new RuntimeException();
}
int r = value % max;
if (r > min && max < min) {
r = r + max - min;
} else if (r < min && max > min) {
r = r + max - min;
}
return r;
}
in the Spinner class (Spinner.java) combined with
/** {@inheritDoc} */
@Override public void decrement(int steps) {
final int min = getMin();
final int max = getMax();
final int newIndex = getValue() - steps * getAmountToStepBy();
setValue(newIndex >= min ? newIndex : (isWrapAround() ? Spinner.wrapValue(newIndex, min, max) + 1 : min));
}
/** {@inheritDoc} */
@Override public void increment(int steps) {
final int min = getMin();
final int max = getMax();
final int currentValue = getValue();
final int newIndex = currentValue + steps * getAmountToStepBy();
setValue(newIndex <= max ? newIndex : (isWrapAround() ? Spinner.wrapValue(newIndex, min, max) - 1 : max));
}
in SpinnerValueFactory.java in the class IntegerSpinnerValueFactory
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Only values between 2 and 10 are allowed ant the order when pressing up should be: 2, 3, 4, 5, 6, 7, 8, 9, 10, 2, 3, 4, and so on....
ACTUAL -
Right now it produces: 2,3,4,5,6,7,8,9,10, 8, 9, 10, 8, 9, 10, 8, 9, 10 and so on....
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
package sample;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Spinner;
import javafx.scene.control.SpinnerValueFactory.IntegerSpinnerValueFactory;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
Spinner<Integer> spinner = new Spinner<>();
IntegerSpinnerValueFactory integerSpinnerValueFactory = new IntegerSpinnerValueFactory(2, 10, 2);
integerSpinnerValueFactory.setWrapAround(true);
spinner.setValueFactory(integerSpinnerValueFactory);
VBox vBox = new VBox();
vBox.getChildren().setAll(spinner);
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(vBox, 300, 275));
primaryStage.show();
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Subclass IntegerSpinnerValueFactory and adapt increment to your needs. In our case, we only need step of size 1, i.e:
package sample;
import javafx.scene.control.SpinnerValueFactory.IntegerSpinnerValueFactory;
public final class IntSpinnerValueFactory extends IntegerSpinnerValueFactory {
public IntSpinnerValueFactory(final int min, final int max) {
super(min, max);
}
public IntSpinnerValueFactory(final int min, final int max, final int initialValue) {
super(min, max, initialValue, 1);
}
@Override
public void increment(int steps) {
final int min = getMin();
final int max = getMax();
final int currentValue = getValue();
final int newIndex = currentValue + steps * getAmountToStepBy();
setValue(
newIndex <= max ? newIndex : (isWrapAround() ? min : max));
}
}
9.0.1, x86_64: "Java SE 9.0.1" /Library/Java/JavaVirtualMachines/jdk-9.0.1.jdk/Contents/Home
ADDITIONAL OS VERSION INFORMATION :
Darwin 16.7.0 Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64 x86_64
A DESCRIPTION OF THE PROBLEM :
The IntegerSpinner does not work as expected. The wrap around does not behave correctly.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the provided source code. Step through the spinner values by pressing the UP button. Note, when pressing the DOWN Button the behaviour is correct. I think the bug is in the following implementation:
static int wrapValue(int value, int min, int max) {
if (max == 0) {
throw new RuntimeException();
}
int r = value % max;
if (r > min && max < min) {
r = r + max - min;
} else if (r < min && max > min) {
r = r + max - min;
}
return r;
}
in the Spinner class (Spinner.java) combined with
/** {@inheritDoc} */
@Override public void decrement(int steps) {
final int min = getMin();
final int max = getMax();
final int newIndex = getValue() - steps * getAmountToStepBy();
setValue(newIndex >= min ? newIndex : (isWrapAround() ? Spinner.wrapValue(newIndex, min, max) + 1 : min));
}
/** {@inheritDoc} */
@Override public void increment(int steps) {
final int min = getMin();
final int max = getMax();
final int currentValue = getValue();
final int newIndex = currentValue + steps * getAmountToStepBy();
setValue(newIndex <= max ? newIndex : (isWrapAround() ? Spinner.wrapValue(newIndex, min, max) - 1 : max));
}
in SpinnerValueFactory.java in the class IntegerSpinnerValueFactory
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Only values between 2 and 10 are allowed ant the order when pressing up should be: 2, 3, 4, 5, 6, 7, 8, 9, 10, 2, 3, 4, and so on....
ACTUAL -
Right now it produces: 2,3,4,5,6,7,8,9,10, 8, 9, 10, 8, 9, 10, 8, 9, 10 and so on....
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
package sample;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Spinner;
import javafx.scene.control.SpinnerValueFactory.IntegerSpinnerValueFactory;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
Spinner<Integer> spinner = new Spinner<>();
IntegerSpinnerValueFactory integerSpinnerValueFactory = new IntegerSpinnerValueFactory(2, 10, 2);
integerSpinnerValueFactory.setWrapAround(true);
spinner.setValueFactory(integerSpinnerValueFactory);
VBox vBox = new VBox();
vBox.getChildren().setAll(spinner);
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(vBox, 300, 275));
primaryStage.show();
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Subclass IntegerSpinnerValueFactory and adapt increment to your needs. In our case, we only need step of size 1, i.e:
package sample;
import javafx.scene.control.SpinnerValueFactory.IntegerSpinnerValueFactory;
public final class IntSpinnerValueFactory extends IntegerSpinnerValueFactory {
public IntSpinnerValueFactory(final int min, final int max) {
super(min, max);
}
public IntSpinnerValueFactory(final int min, final int max, final int initialValue) {
super(min, max, initialValue, 1);
}
@Override
public void increment(int steps) {
final int min = getMin();
final int max = getMax();
final int currentValue = getValue();
final int newIndex = currentValue + steps * getAmountToStepBy();
setValue(
newIndex <= max ? newIndex : (isWrapAround() ? min : max));
}
}
- relates to
-
JDK-8242553 IntegerSpinner and DoubleSpinner do not wrap around values correctly in some cases
- Resolved
-
JDK-8331214 Doc: update spec for SpinnerFactory classes
- Resolved