Search code examples
javaswingjspinnerevent-listener

Increase step size on JSpinner when a certain key is pressed


I want to be able to increase the step size on JSpinner when a certain key is pressed. What i tried so far was to put KeyListener on my spinner and when a certain key is pressed, change the value of the step size. When the key is release it should return to it's default.

I think i don't have to directly put the KeyListener onto the JSpinner but rather on its button.

I have honnestly no idea on how to achieve that. What confuse me here is the double Listener.

Here is the code i wrote for the KeyListener :

public class SpinnerKeyIncrement implements KeyListener {

    JSpinner spinner;
    SpinnerNumberModel spinnerModel;

    public SpinnerKeyIncrement(JSpinner s) {
        this.spinner = s;
        if(spinnerModel == null)
            spinnerModel = (SpinnerNumberModel) spinner.getModel();
    }


    @Override
    public void keyPressed(KeyEvent e) {
        if(e.getKeyCode() == KeyEvent.VK_CONTROL) {
            spinnerModel.setStepSize(.10);
        }
        else if(e.getKeyCode() == KeyEvent.VK_SHIFT) {
            spinnerModel.setStepSize(1);
        }

    }

    @Override
    public void keyReleased(KeyEvent e) {
        spinnerModel.setStepSize(0.01);

    }

    @Override
    public void keyTyped(KeyEvent e) {}

    }
}

Solution

  • Again, I'd use Key Bindings to solve this, and I'd use bindings bound to the JSpinner's InputMap that's active when it has focus:

      InputMap inputMap = spinner.getInputMap(JComponent.WHEN_FOCUSED);
    

    A working small example program could look like this:

    import java.awt.event.ActionEvent;
    import java.awt.event.KeyEvent;
    import javax.swing.*;
    
    @SuppressWarnings("serial")
    public class ChangeStepSize extends JPanel {
       private static final String DOUBLE_STEP = "double step";
       public static final int SMALL_STEP_SIZE = 1;
       public static final int BIG_STEP_SIZE = 10;
    
       // bind to the "d" key, but you could use any key you'd like
       private static final int SPECIAL_KEY = KeyEvent.VK_D;
       private SpinnerNumberModel numberModel = new SpinnerNumberModel(50, 0, 100, 1);
       private JSpinner spinner = new JSpinner(numberModel);
    
       public ChangeStepSize() {
          add(spinner);
    
          // set up key bindings. First get InputMap and ActionMap
          InputMap inputMap = spinner.getInputMap(JComponent.WHEN_FOCUSED);
          ActionMap actionMap = spinner.getActionMap();
    
          // next set bindings for when key is pressed
          boolean onKeyRelease = false;
          KeyStroke keyStroke = KeyStroke.getKeyStroke(SPECIAL_KEY, 0, onKeyRelease);
          inputMap.put(keyStroke, DOUBLE_STEP + onKeyRelease);
          actionMap.put(DOUBLE_STEP + onKeyRelease, new DoubleStepAction(onKeyRelease));
    
          // next set bindings for when key is released
          onKeyRelease = true;
          keyStroke = KeyStroke.getKeyStroke(SPECIAL_KEY, 0, onKeyRelease);
          inputMap.put(keyStroke, DOUBLE_STEP + onKeyRelease);
          actionMap.put(DOUBLE_STEP + onKeyRelease, new DoubleStepAction(onKeyRelease));
       }
    
       // our Action is passed in a parameter to indicate which state it
       // gets activated in: key press or key release
       private class DoubleStepAction extends AbstractAction {
          private boolean onKeyRelease;
    
          public DoubleStepAction(boolean onKeyRelease) {
             this.onKeyRelease = onKeyRelease;
          }
    
          @Override
          public void actionPerformed(ActionEvent e) {
             if (onKeyRelease) {
                numberModel.setStepSize(SMALL_STEP_SIZE);
             } else {
                numberModel.setStepSize(BIG_STEP_SIZE);
             }
          }
       }
    
       private static void createAndShowGui() {
          ChangeStepSize mainPanel = new ChangeStepSize();
    
          JFrame frame = new JFrame("ChangeStepSize");
          frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
          frame.getContentPane().add(mainPanel);
          frame.pack();
          frame.setLocationByPlatform(true);
          frame.setVisible(true);
       }
    
       public static void main(String[] args) {
          SwingUtilities.invokeLater(new Runnable() {
             public void run() {
                createAndShowGui();
             }
          });
       }
    }