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