Example:Keyboard navigation with shifted key combinations and number pad keys using KeyEvents

edited April 2016 in Questions about Code

The following code is a contrived example that shows how to handle special keys, and shifted key combinations. The ellipse moves either a small increment , (arrow keys) or large increment, (Page up, Page Down, shifted arrows) or jumps (Home, End, Tab, Shift-Tab)

I couldn't find a single example that addressed all the key handling aspects presented, This code is cobbled together from a number of places. Sorry of the any lack of attribution. (Note: there is no frame edge detection as this is just a simple demo.)

The example includes:

 * TAB, SHIFT-TAB
 * Arrow keys, SHIFTED Arrow keys
 * HOME
 * END
 * Page Up, Page Down
 * Num-Lock  1-9 keys

as well as a KeyPrefix() function that composes the text for any shifted keys, as in SHIFT-CONTROL-ALT.

import java.awt.event.KeyEvent;

int largeChangeX;
int smallChangeX;
int largeChangeY;
int smallChangeY;
int xPos;
int yPos;
//
// keep a map of the pressed keys for detecting shifted combinations
//
boolean[] keys = new boolean[526];

void setup()
{
  largeChangeX = width/20;
  smallChangeX = largeChangeX/5;
  largeChangeY = height/10;
  smallChangeY = largeChangeY/5;
  size(500,400);
  xPos = (int)random(width);
  yPos = (int)random(height);
}
void draw()
{
  background(123);
  fill(24, 123, 234);
  ellipse(xPos, yPos, 200, 100);
  noLoop();
}

void adjustPosition(int xOffset, int yOffset)
{
  xPos += xOffset;
  yPos += yOffset;
  loop();
}
void keyPressed() 
{
  keys[keyCode] = true;
  //println(KeyEvent.getKeyText(keyCode));
  whatKey(key, keyCode);
}
void keyReleased()
{ 
  keys[keyCode] = false;
}

//
// simple approach to dealing with shifted keys
//
boolean checkKey(int k)
{
  if (keys.length >= k) {
    return keys[k];
  }
  return false;
}
//
// compose key description prefix of shifted keys
//
String keyPrefix()
{
  String prefix;
  boolean prefixPresent;
  prefix = "";
  prefixPresent = false;
  if (checkKey(KeyEvent.VK_SHIFT)== true)
  {
    prefix += "SHIFT";
    prefixPresent = true;
  }
  if (checkKey(KeyEvent.VK_CONTROL) == true)
  {
    prefix += (prefixPresent == true ? "-" : "") +"CTRL";
    prefixPresent = true;
  }
  if (checkKey(KeyEvent.VK_ALT) == true)
  {
    prefix += (prefixPresent == true ? "-" : "") +"ALT"; 
    prefixPresent = true;
  }
  prefix += (prefixPresent == true ? " " : "");
  return prefix;
}
 //
//
//
void whatKey(int key, int keycode)
{
  int adjustmentX;
  int adjustmentY;
  String what;
  adjustmentX = adjustmentY = 0;
  //
  // show what key has struct
  //
  what = keyPrefix() + KeyEvent.getKeyText(keyCode);
  println("Key is ==>"+what+"<==");
  switch(keycode)
  {
  case java.awt.event.KeyEvent.VK_PAGE_UP:      
    adjustmentY = -largeChangeY;
    break;
  case java.awt.event.KeyEvent.VK_PAGE_DOWN:      
    adjustmentY = largeChangeY;
    break;
  case java.awt.event.KeyEvent.VK_END:
    adjustmentX = width - xPos;      
    break;
  case java.awt.event.KeyEvent.VK_HOME:
    adjustmentX = -xPos;
    break;
  case java.awt.event.KeyEvent.VK_LEFT:
    if (checkKey(KeyEvent.VK_SHIFT)== true)
    {
       adjustmentX = -largeChangeX;
    }
    else
    {
     adjustmentX = -smallChangeX;     
    }
    break;
  case java.awt.event.KeyEvent.VK_UP:
    if (checkKey(KeyEvent.VK_SHIFT)== true)
    {
       adjustmentY = -largeChangeX;
    }
    else
    {
     adjustmentY = -smallChangeX;     
    }
    break;
  case java.awt.event.KeyEvent.VK_RIGHT:
    if (checkKey(KeyEvent.VK_SHIFT)== true)
    {
       adjustmentX = largeChangeX;
    }
    else
    {
     adjustmentX = smallChangeX;     
    }
    break;
  case java.awt.event.KeyEvent.VK_DOWN:
    if (checkKey(KeyEvent.VK_SHIFT)== true)
    {
       adjustmentY = largeChangeX;
    }
    else
    {
     adjustmentY = smallChangeX;     
    }
    break;
  case java.awt.event.KeyEvent.VK_NUMPAD1:
    adjustmentX = width -xPos;
    break;
  case java.awt.event.KeyEvent.VK_NUMPAD2:
    adjustmentY = +smallChangeY;
    break;
  case java.awt.event.KeyEvent.VK_NUMPAD3:
    adjustmentY = +largeChangeY;
    break;
  case java.awt.event.KeyEvent.VK_NUMPAD4:
    adjustmentX = -smallChangeX;
    break;
  case java.awt.event.KeyEvent.VK_NUMPAD5:
    break;
  case java.awt.event.KeyEvent.VK_NUMPAD6:
    adjustmentX = smallChangeX;
    break;
  case java.awt.event.KeyEvent.VK_NUMPAD7:
    adjustmentX = -xPos;
    break;
  case java.awt.event.KeyEvent.VK_NUMPAD8:
    adjustmentY = -smallChangeY;
    break;
  case java.awt.event.KeyEvent.VK_NUMPAD9:
    adjustmentY = +largeChangeY;
    break;
  case java.awt.event.KeyEvent.VK_TAB:
    if (checkKey(KeyEvent.VK_SHIFT)== true) 
    {
      adjustmentX = width-xPos;
      adjustmentY = height+yPos;
    } else
    {
      adjustmentX = -xPos;
      adjustmentY = -yPos;
    }
    break;
  }
  adjustPosition(adjustmentX, adjustmentY);
}

Answers

  • Answer ✓

    Some remarks on this code:

    • Since you import it, why do you put the fully qualified names for KeyEvent? Could be case KeyEvent.VK_TAB:. Even better with a static import, but these don't work in Processing... :-(
    • Since it is educational, you should put the call to size() at the start of setup(), as advised in the doc. Not critical here, but that's a good practice...
    • In checkKey, the test is incorrect: if K == key.length, you will have trouble...
    • You use an array of 526 entries (where this number comes from?) to check the state of three modifier keys? Three boolean variables would have been enough. Such array is more used for multi-key presses (like going to a diagonal).
    • I know lot of examples do that, but I prefer booleans to be used as such: I never test if (xxx == true) or if (xxx == false), rather if (xxx) or if (!xxx). Works better with a little renaming: if (isKeyPressed(KeyEvent.VK_SHIFT)).

    Beside these remarks, the demo is useful, thanks for sharing.

  • ... static import, but these don't work in Processing...

    import static always worked, even in Processing 1.5.1! As proof of it:

    /** 
     * No Repeat ID Input (v1.10)
     * by GoToLoop (2013/Nov)
     * 
     * forum.processing.org/two/discussion/869
     * /check-array-contents-with-arraylist
     */
    
    import static javax.swing.JOptionPane.*;
    
    final StringList ids = new StringList( new String[] {
      "Eric", "Beth", "Katniss"
    } 
    );
    
    void draw() {
      println(ids);
    
      final String id = showInputDialog("Please enter new ID");
    
      if (id == null)   exit();
    
      else if ("".equals(id))
        showMessageDialog(null, "Empty ID Input!!!", 
        "Alert", ERROR_MESSAGE);
    
      else if (ids.hasValue(id))
        showMessageDialog(null, "ID \"" + id + "\" exists already!!!", 
        "Alert", ERROR_MESSAGE);
    
      else {
        showMessageDialog(null, "ID \"" + id + "\" successfully added!!!", 
        "Info", INFORMATION_MESSAGE);
    
        ids.append(id);
      }
    }
    
  • edited July 2014

    Cut and paste from elsewhere. The array size of 526 was a direct copy from someone else's code. I expect it was originally a typo for 256.

    The key pressed handling code is generic for checking the state of multiple keys in general. The example just takes advantage on the SHIFT but includes the prefix keyPrefix() function to help in deciphering keyboard behavior.

    Here is the code incorporating the suggestions

    import java.awt.event.KeyEvent;
    
    int largeChangeX;
    int smallChangeX;
    int largeChangeY;
    int smallChangeY;
    int xPos;
    int yPos;
    //
    // keep a map of the pressed keys for detecting shifted combinations
    //
    boolean[] keys = new boolean[526];
    
    void setup()
    {
      size(500,400);
      largeChangeX = width/20;
      smallChangeX = largeChangeX/5;
      largeChangeY = height/10;
      smallChangeY = largeChangeY/5;
      xPos = (int)random(width);
      yPos = (int)random(height);
    }
    void draw()
    {
      background(123);
      fill(24, 123, 234);
      ellipse(xPos, yPos, 200, 100);
      noLoop();
    }
    
    void adjustPosition(int xOffset, int yOffset)
    {
      xPos += xOffset;
      yPos += yOffset;
      loop();
    }
    void keyPressed() 
    {
      keys[keyCode] = true;
      whatKey(key, keyCode);
    }
    void keyReleased()
    { 
      keys[keyCode] = false;
    }
    
    //
    // simple approach to dealing with shifted keys
    //
    boolean isKeyPressed(int k)
    {
      if (keys.length >= k) 
      {
        return keys[k];
      }
      return false;
    }
    //
    // compose key description prefix of shifted keys
    //
    String keyPrefix()
    {
      String prefix;
      boolean prefixPresent;
      prefix = "";
      prefixPresent = false;
    
      if (isKeyPressed(KeyEvent.VK_SHIFT))
      {
        prefix += "SHIFT";
        prefixPresent = true;
      }
      if (isKeyPressed(KeyEvent.VK_CONTROL))
      {
        prefix += (prefixPresent == true ? "-" : "") +"CTRL";
        prefixPresent = true;
      }
      if (isKeyPressed(KeyEvent.VK_ALT))
      {
        prefix += (prefixPresent == true ? "-" : "") +"ALT"; 
        prefixPresent = true;
      }
      prefix += (prefixPresent == true ? " " : "");
      return prefix;
    }
    //
    //
    //
    void whatKey(int key, int keycode)
    {
      int adjustmentX;
      int adjustmentY;
      String what;
      adjustmentX = adjustmentY = 0;
      //
      // show what key has struct
      //
      what = keyPrefix() + KeyEvent.getKeyText(keyCode);
         println("Key is ==>"+what+"<==");
    
      switch(keycode)
      {
      case KeyEvent.VK_PAGE_UP:      
        adjustmentY = -largeChangeY;
        break;
      case KeyEvent.VK_PAGE_DOWN:      
        adjustmentY = largeChangeY;
        break;
      case KeyEvent.VK_END:
        adjustmentX = width - xPos;      
        break;
      case KeyEvent.VK_HOME:
        adjustmentX = -xPos;
        break;
      case KeyEvent.VK_LEFT:
         if (isKeyPressed(KeyEvent.VK_SHIFT))
        {
           adjustmentX = -largeChangeX;
        }
        else
        {
         adjustmentX = -smallChangeX;     
        }
        break;
      case KeyEvent.VK_UP:
        if (isKeyPressed(KeyEvent.VK_SHIFT))
        {
           adjustmentY = -largeChangeX;
        }
        else
        {
         adjustmentY = -smallChangeX;     
        }
        break;
      case KeyEvent.VK_RIGHT:
        if (isKeyPressed(KeyEvent.VK_SHIFT))
        {
           adjustmentX = largeChangeX;
        }
        else
        {
         adjustmentX = smallChangeX;     
        }
        break;
      case KeyEvent.VK_DOWN:
        if (isKeyPressed(KeyEvent.VK_SHIFT))
        {
           adjustmentY = largeChangeX;
        }
        else
        {
         adjustmentY = smallChangeX;     
        }
        break;
      case KeyEvent.VK_NUMPAD1:
        adjustmentX = width -xPos;
        break;
      case KeyEvent.VK_NUMPAD2:
        adjustmentY = +smallChangeY;
        break;
      case KeyEvent.VK_NUMPAD3:
        adjustmentY = +largeChangeY;
        break;
      case KeyEvent.VK_NUMPAD4:
        adjustmentX = -smallChangeX;
        break;
      case KeyEvent.VK_NUMPAD5:
        break;
      case KeyEvent.VK_NUMPAD6:
        adjustmentX = smallChangeX;
        break;
      case KeyEvent.VK_NUMPAD7:
        adjustmentX = -xPos;
        break;
      case KeyEvent.VK_NUMPAD8:
        adjustmentY = -smallChangeY;
        break;
      case KeyEvent.VK_NUMPAD9:
        adjustmentY = +largeChangeY;
        break;
      case KeyEvent.VK_TAB:
        if (isKeyPressed(KeyEvent.VK_SHIFT))
        {
          adjustmentX = width-xPos;
          adjustmentY = height+yPos;
        } 
        else
        {
          adjustmentX = -xPos;
          adjustmentY = -yPos;
        }
        break;
      }
      adjustPosition(adjustmentX, adjustmentY);
    }
    
  • You should check array boundary in order to avoid exception warnings:

    void keyPressed() {
      if (keyCode < keys.length) {
        keys[keyCode] = true;
        whatKey(key, keyCode);
      }
    }
    
  • Ah, I went too quickly. The import static works, but Processing displays the message "No library found for static java.awt.event.KeyEvent" which is misleading / disturbing...

Sign In or Register to comment.