diff --git a/pom.xml b/pom.xml
index e5d64f4..f76072a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.scijava
pom-scijava
- 28.0.0
+ 30.0.0
@@ -108,8 +108,7 @@
1.3.2
0.1
- 5.2
- 0.1.7
+ 2.87.0
@@ -147,7 +146,6 @@
com.miglayout
miglayout-swing
- ${miglayout-swing.version}
net.sourceforge.jdatepicker
@@ -161,5 +159,13 @@
junit
test
+
+
+ org.scijava
+ scijava-common
+ tests
+ test
+
+
diff --git a/src/main/java/org/scijava/ui/swing/widget/SwingNumberWidget.java b/src/main/java/org/scijava/ui/swing/widget/SwingNumberWidget.java
index e833419..999cf39 100644
--- a/src/main/java/org/scijava/ui/swing/widget/SwingNumberWidget.java
+++ b/src/main/java/org/scijava/ui/swing/widget/SwingNumberWidget.java
@@ -41,6 +41,8 @@
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.text.ParsePosition;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.Hashtable;
import javax.swing.JComponent;
@@ -62,6 +64,7 @@
import org.scijava.widget.InputWidget;
import org.scijava.widget.NumberWidget;
import org.scijava.widget.WidgetModel;
+import org.scijava.widget.WidgetStyle;
/**
* Swing implementation of number chooser widget.
@@ -119,6 +122,12 @@ else if (model.isStyle(NumberWidget.SLIDER_STYLE)) {
final SpinnerNumberModel spinnerModel =
new SpinnerNumberModelFactory().createModel(value, min, max, stepSize);
spinner = new JSpinner(spinnerModel);
+ String format = WidgetStyle.getStyleModifier(model.getItem().getWidgetStyle(), "format");
+ if (format == null) {
+ format = suitableFormat(value, stepSize, min, max);
+ }
+ spinner.setEditor(new JSpinner.NumberEditor(spinner, format));
+
Dimension spinnerSize = spinner.getSize();
spinnerSize.width = 50;
spinner.setPreferredSize(spinnerSize);
@@ -329,6 +338,20 @@ private void syncSliders() {
}
}
+ /** Generate a suitable format pattern. */
+ private String suitableFormat(Number... values) {
+ Integer maxScale = Arrays.stream(values)
+ .map(n -> new BigDecimal("" + n.doubleValue()).stripTrailingZeros().scale()).max(Integer::compare)
+ .get();
+ return formatForScale(maxScale);
+ }
+
+ /** Generate a format pattern with sufficient number of decimals. */
+ private String formatForScale(int scale) {
+ if (scale <= 0) return "0";
+ return "0." + String.join("", Collections.nCopies(scale, "0"));
+ }
+
// -- AbstractUIInputWidget methods ---
@Override
diff --git a/src/test/java/org/scijava/ui/swing/widget/SwingNumberWidgetDemo.java b/src/test/java/org/scijava/ui/swing/widget/SwingNumberWidgetDemo.java
new file mode 100644
index 0000000..45b6c88
--- /dev/null
+++ b/src/test/java/org/scijava/ui/swing/widget/SwingNumberWidgetDemo.java
@@ -0,0 +1,69 @@
+package org.scijava.ui.swing.widget;
+
+import org.scijava.Context;
+import org.scijava.command.Command;
+import org.scijava.command.CommandService;
+import org.scijava.plugin.Parameter;
+import org.scijava.ui.UIService;
+
+public class SwingNumberWidgetDemo implements Command {
+
+ @Parameter(persist = false)
+ private Double a = 0.0000123;
+
+ @Parameter(persist = false)
+ private Double b = 0.000123;
+
+ @Parameter(persist = false)
+ private Double c = 0.00123;
+
+ @Parameter(persist = false)
+ private Double d = 0.0123;
+
+ @Parameter(persist = false)
+ private Double e = 0.123;
+
+ @Parameter(persist = false)
+ private Double f = 1.23;
+
+ @Parameter(persist = false)
+ private Double g = 123d;
+
+ @Parameter(min = "0.0", max = "10.0", stepSize = "0.001", persist = false)
+ private Double h = 1d;
+
+ @Parameter(style = "format:#.##", persist = false)
+ private Double i = 0.0123;
+
+ @Parameter(style = "format:#.00", persist = false)
+ private Double j = 0.0123;
+
+ @Parameter(style = "format:#####.#####", persist = false)
+ private Double k = 123.45;
+
+ @Parameter(style = "format:00000.00000", persist = false)
+ private Double l = 123.45;
+
+ @Parameter(style = "slider", min = "0", max = "10", stepSize = "0.001", persist = false)
+ private Double m = 1d;
+
+ @Parameter(style = "slider,format:0.0000", min = "0", max = "10", stepSize = "0.001", persist = false)
+ private Double n = 1d;
+
+ @Parameter(style = "scroll bar", min = "0", max = "10", stepSize = "0.001", persist = false)
+ private Double o = 1d;
+
+ @Parameter(style = "scroll bar,format:0.0000", min = "0", max = "10", stepSize = "0.001", persist = false)
+ private Double p = 1d;
+
+ @Override
+ public void run() {
+ // Nothing to do.
+ }
+
+ public static void main(final String... args) throws Exception {
+ Context context = new Context();
+ context.service(UIService.class).showUI();
+ context.service(CommandService.class).run(SwingNumberWidgetDemo.class, true);
+ }
+}
diff --git a/src/test/java/org/scijava/ui/swing/widget/SwingNumberWidgetTest.java b/src/test/java/org/scijava/ui/swing/widget/SwingNumberWidgetTest.java
new file mode 100644
index 0000000..500aaa7
--- /dev/null
+++ b/src/test/java/org/scijava/ui/swing/widget/SwingNumberWidgetTest.java
@@ -0,0 +1,148 @@
+/*-
+ * #%L
+ * SciJava UI components for Java Swing.
+ * %%
+ * Copyright (C) 2010 - 2020 SciJava developers.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+package org.scijava.ui.swing.widget;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.StringReader;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collections;
+
+import javax.swing.JSpinner;
+import javax.swing.JSpinner.NumberEditor;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.scijava.Context;
+import org.scijava.module.Module;
+import org.scijava.module.ModuleException;
+import org.scijava.module.ModuleItem;
+import org.scijava.module.ModuleService;
+import org.scijava.script.ScriptInfo;
+import org.scijava.script.ScriptService;
+import org.scijava.util.ListUtils;
+import org.scijava.widget.InputWidget;
+import org.scijava.widget.WidgetModel;
+import org.scijava.widget.WidgetService;
+
+public class SwingNumberWidgetTest {
+
+ private Context context;
+ private ModuleService moduleService;
+ private WidgetService widgetService;
+
+ @Before
+ public void setUp() {
+ context = new Context();
+ moduleService = context.service(ModuleService.class);
+ widgetService = context.service(WidgetService.class);
+ }
+
+ @After
+ public void tearDown() {
+ context.dispose();
+ }
+
+ @Test
+ public void test() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, ModuleException {
+ String[] expecteds = getExpectedValues();
+ Field spinnerField = SwingNumberWidget.class.getDeclaredField("spinner");
+ spinnerField.setAccessible(true);
+
+ String script = createScript();
+ ScriptInfo info = new ScriptInfo(context, ".bsizes", new StringReader(script));
+ //System.err.println(moduleService);
+ Module module = moduleService.createModule(info);
+ //Module module = info.createModule();
+ Iterable> inputs = info.inputs();
+ SwingInputPanel inputPanel = new SwingInputPanel();
+ int i = 0;
+ for (ModuleItem> item : inputs) {
+ WidgetModel model = widgetService.createModel(inputPanel, module, item, Collections.EMPTY_LIST);
+ InputWidget, ?> inputWidget = widgetService.create(model);
+ assertTrue(inputWidget instanceof SwingNumberWidget);
+
+ JSpinner spinner = (JSpinner) spinnerField.get(inputWidget);
+ NumberEditor editor = (NumberEditor) spinner.getEditor();
+ assertEquals("Format (index " + i + ")", expecteds[i++], editor.getTextField().getText());
+ }
+ }
+
+ private String[] getExpectedValues() {
+ return new String[] {
+ "0.0000123",
+ "0.000123",
+ "0.00123",
+ "0.0123",
+ "0.123",
+ "1.23",
+ "123",
+ "1.000",
+ "0.01",
+ ".01",
+ "123.45",
+ "00123.45000",
+ "1.000",
+ "1.0000",
+ "1.000",
+ "1.0000"
+ };
+ }
+
+ private String createScript() {
+ final String script = "// Automatically generated format\n" +
+ "#@ Double (value=0.0000123, persist=false) a\n" +
+ "#@ Double (value=0.000123, persist=false) b\n" +
+ "#@ Double (value=0.00123, persist=false) c\n" +
+ "#@ Double (value=0.0123, persist=false) d\n" +
+ "#@ Double (value=0.123, persist=false) e\n" +
+ "#@ Double (value=1.23, persist=false) f\n" +
+ "#@ Double (value=123, persist=false) g\n" +
+ "#@ Double (value=1, min=0.0, max=10.0, stepSize=0.001, persist=false) h\n" +
+ "\n" +
+ "// Specified format\n" +
+ "#@ Double (value=0.0123, persist=false, style=\"format:#.##\") i\n" +
+ "#@ Double (value=0.0123, persist=false, style=\"format:#.00\") j\n" +
+ "#@ Double (value=123.45, persist=false, style=\"format:#####.#####\") k\n" +
+ "#@ Double (value=123.45, persist=false, style=\"format:00000.00000\") l\n" +
+ "\n" +
+ "// Sliders and scroll bars\n" +
+ "#@ Double (value=1, min=0, max=10, stepSize=0.001, persist=false, style=slider) m\n" +
+ "#@ Double (value=1, min=0, max=10, stepSize=0.001, persist=false, style=\"slider,format:0.0000\") n\n" +
+ "#@ Double (value=1, min=0, max=10, stepSize=0.001, persist=false, style=\"scroll bar\") o\n" +
+ "#@ Double (value=1, min=0, max=10, stepSize=0.001, persist=false, style=\"scroll bar,format:0.0000\") p\n" +
+ "";
+ return script;
+ }
+
+}