package greenfoot.gui.input;

import javafx.scene.input.KeyCode;
import threadchecker.OnThread;
import threadchecker.Tag;

import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;


| Manage keyboard input, to allow Greenfoot programs to poll for | keystrokes. Keystrokes will be delivered on the GUI event thread, | but may be polled from another thread. | | <p>The following key names are recognized: | up, down, left, right (cursor keys); enter, space, tab, escape, backspace, | F1-F12. | | @author Davin McCall | public class KeyboardManager { private String lastKeyTyped; private final Set<String> keyLatched = new HashSet<>(); private final Set<String> keyDown = new HashSet<>();
| Do we think that a numlock key is present? | private boolean hasNumLock = true;
| Constructor for a KeyboardManager. Key events must be delivered | from an external source. | public KeyboardManager() { }
| Clear the latched state of keys which were down, but are no longer | down. | @OnThread(Tag.Simulation) public synchronized void clearLatchedKeys() { for (Iterator<String> i = keyLatched.iterator(); i.hasNext(); ) { String keyCode = i.next(); if (!keyDown.contains(keyCode)) { i.remove(); } } }
| Get the last key pressed, as a String key name identifying the key. | @OnThread(Tag.Simulation) public synchronized String getKey() { String r = lastKeyTyped; lastKeyTyped = null; return r; }
| Check whether a key, identified by a key name (String), | is currently down (or latched). | | @param key The name of the key to check | @return True if the key is currently down, or was down since | it was last checked; false otherwise. | @OnThread(Tag.Simulation) public synchronized boolean isKeyDown(String key) { key = key.toLowerCase(); boolean pressed = keyDown.contains(key) || keyLatched.contains(key); keyLatched.remove(key); return pressed; }
| Notifies that a key has been pressed. | @param keyCode The KeyCode from KeyEvent.getCode() | @param keyText The text from KeyEvent.getText() | public synchronized void keyPressed(KeyCode keyCode, String keyText) { String keyName = getKeyName(keyCode, keyText); keyLatched.add(keyName); keyDown.add(keyName); }
| Notifies that a key has been released. | @param keyCode The KeyCode from KeyEvent.getCode() | @param keyText The text from KeyEvent.getText() | public synchronized void keyReleased(KeyCode keyCode, String keyText) { String keyName = getKeyName(keyCode, keyText); keyDown.remove(keyName); lastKeyTyped = keyName; }
| Translate the "key pad" directional keys according to the status of numlock, |* and otherwise translate a KeyCode+text into a Greenfoot key name. * * @param keycode The original keycode * @param keyText The text from the original keyboard event | @return The translated key name | private String getKeyName(KeyCode keycode, String keyText) { if (keycode.ordinal() >= KeyCode.NUMPAD0.ordinal() && keycode.ordinal() <= KeyCode.NUMPAD9.ordinal()) { return "" + (char)('0' + (keycode.ordinal() - KeyCode.NUMPAD0.ordinal())); } boolean numlock = true; if (hasNumLock) { try { numlock = Toolkit.getDefaultToolkit().getLockingKeyState(KeyEvent.VK_NUM_LOCK); } catch (UnsupportedOperationException usoe) { hasNumLock = false; } } if (numlock) { if (keycode == KeyCode.KP_UP) { keycode = KeyCode.DIGIT8; } else if (keycode == KeyCode.KP_DOWN) { keycode = KeyCode.DIGIT2; } else if (keycode == KeyCode.KP_LEFT) { keycode = KeyCode.DIGIT4; } else if (keycode == KeyCode.KP_RIGHT) { keycode = KeyCode.DIGIT6; } } else { if (keycode == KeyCode.KP_UP) { keycode = KeyCode.UP; } else if (keycode == KeyCode.KP_DOWN) { keycode = KeyCode.DOWN; } else if (keycode == KeyCode.KP_LEFT) { keycode = KeyCode.LEFT; } else if (keycode == KeyCode.KP_RIGHT) { keycode = KeyCode.RIGHT; } } switch (keycode) { case ESCAPE: return "escape"; case BACK_SPACE: return "backspace"; case QUOTE: return "\'"; case CONTROL: return "control"; default: break; } if (!keyText.isEmpty()) { switch (keyText) { case "\r": case "\n": return "enter"; case "\t": return "tab"; case "\b": return "backspace"; case " ": return "space"; case "\u001B": return "escape"; } return keyText.toLowerCase(); } else { return keycode.getName().toLowerCase(); } }
| Notifies that a key has been typed. | @param keyCode The KeyCode from KeyEvent.getCode() | @param keyText The text from KeyEvent.getText() | public synchronized void keyTyped(KeyCode keyCode, String keyText) { String keyName = getKeyName(keyCode, keyText); if (!keyName.isEmpty() && !keyName.equals("undefined")) { lastKeyTyped = keyName; } } public void focusGained() { }
| If we loose focus, we should treat all keys as not pressed anymore | public void focusLost() { releaseAllKeys(); }
| Release all the keys. | private synchronized void releaseAllKeys() { keyDown.clear(); keyLatched.clear(); } }
top, use, map, class KeyboardManager

.   KeyboardManager
.   clearLatchedKeys
.   getKey
.   isKeyDown
.   keyPressed
.   keyReleased
.   getKeyName
.   keyTyped
.   focusGained
.   focusLost
.   releaseAllKeys




289 neLoCode + 31 LoComm