diff --git a/src/nl/jeroenhoek/josm/gridify/ui/GridSizePanel.java b/src/nl/jeroenhoek/josm/gridify/ui/GridSizePanel.java index 7e4772d..96e215c 100644 --- a/src/nl/jeroenhoek/josm/gridify/ui/GridSizePanel.java +++ b/src/nl/jeroenhoek/josm/gridify/ui/GridSizePanel.java @@ -26,8 +26,8 @@ public GridSizePanel(ChangeCallback changeCallback, int rows, int columns) { this.columns = columns; setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); - spinnerRows = new PositiveSpinner(rows); - spinnerColumns = new PositiveSpinner(columns); + spinnerRows = new PositiveSpinner(rows, this::setRowCount); + spinnerColumns = new PositiveSpinner(columns, this::setColumnCount); JLabel xLabel = new JLabel("×"); xLabel.setFont(new Font("Monospaced", Font.PLAIN, 18)); @@ -70,6 +70,11 @@ void nudgeColumnCount(Nudge direction) { } } + @Override + public boolean requestFocusInWindow() { + return this.spinnerRows.requestFocusInWindow(); + } + public int getRowCount() { return this.rows; } diff --git a/src/nl/jeroenhoek/josm/gridify/ui/GridifySettingsDialog.java b/src/nl/jeroenhoek/josm/gridify/ui/GridifySettingsDialog.java index 453f3f0..0f6df81 100644 --- a/src/nl/jeroenhoek/josm/gridify/ui/GridifySettingsDialog.java +++ b/src/nl/jeroenhoek/josm/gridify/ui/GridifySettingsDialog.java @@ -111,6 +111,8 @@ public void setupDialog() { setDefaultButton(1); super.setupDialog(); + + gridSizePanel.requestFocusInWindow(); } @Override diff --git a/src/nl/jeroenhoek/josm/gridify/ui/PositiveSpinner.java b/src/nl/jeroenhoek/josm/gridify/ui/PositiveSpinner.java index a189f74..5807d98 100644 --- a/src/nl/jeroenhoek/josm/gridify/ui/PositiveSpinner.java +++ b/src/nl/jeroenhoek/josm/gridify/ui/PositiveSpinner.java @@ -1,20 +1,29 @@ // License: GPL. For details, see LICENSE file. package nl.jeroenhoek.josm.gridify.ui; -import javax.swing.JFormattedTextField; -import javax.swing.JSpinner; -import javax.swing.SpinnerNumberModel; -import java.awt.Dimension; -import java.awt.Font; +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import java.awt.*; +import java.awt.event.FocusEvent; +import java.text.ParseException; +import java.util.Objects; /** * A {@link JSpinner} limited to a range of 1 to 1000. This class also adds a mouse wheel listener allowing users to * scroll the value up or down. */ public class PositiveSpinner extends JSpinner { - public PositiveSpinner(int defaultValue) { + + private final JFormattedTextField field; + private final ValueChanged changeCallback; + private Integer lastValue; + + public PositiveSpinner(int defaultValue, ValueChanged changeCallback) { super(new SpinnerNumberModel(defaultValue, 1, 1000, 1)); - JFormattedTextField field = ((DefaultEditor) getEditor()).getTextField(); + this.changeCallback = changeCallback; + DefaultEditor editor = (DefaultEditor) getEditor(); + field = editor.getTextField(); field.setColumns(3); field.setFont(new Font("Monospaced", Font.PLAIN, 18)); @@ -40,10 +49,88 @@ public PositiveSpinner(int defaultValue) { setValue(newValue); } }); + + // Select the input for easy editing when the field receives focus. + field.addFocusListener(new FocusListener()); + + // Let our parent know when the field value changes for live updates. + field.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + callbackIfChanged(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + callbackIfChanged(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + callbackIfChanged(); + } + }); + + this.lastValue = (Integer) getValue(); + } + + void callbackIfChanged() { + Integer newValue; + try { + newValue = Integer.parseInt(field.getText()); + } catch (NumberFormatException e) { + // Ignore. + return; + } + + if (!Objects.equals(newValue, lastValue)) { + SwingUtilities.invokeLater(() -> { + this.lastValue = newValue; + changeCallback.onChange(newValue); + }); + } } @Override public Dimension getMaximumSize() { return getPreferredSize(); } + + @Override + public boolean requestFocusInWindow() { + return this.field.requestFocusInWindow(); + } + + /** + * This should work on most platforms. + */ + static class FocusListener implements java.awt.event.FocusListener { + // Thanks to mKorbel (https://stackoverflow.com/users/714968/mkorbel) for this instructive snippet: + // https://stackoverflow.com/questions/20971050/jspinner-autoselect-onfocus/20971713#20971713 + + @Override + public void focusGained(FocusEvent e) { + focus(e); + } + + @Override + public void focusLost(FocusEvent e) { + focus(e); + } + + private void focus(FocusEvent e) { + final Component component = e.getComponent(); + if (component instanceof JFormattedTextField) { + JFormattedTextField field = (JFormattedTextField) component; + SwingUtilities.invokeLater(() -> { + field.setText(field.getText()); + field.selectAll(); + }); + } + } + } + + interface ValueChanged { + void onChange(int value); + } }