I've some trouble to understand the key event in SWT. My program runs under Windows 8.1 + Java 8.
For example I have a text field and try to handle KeyUp:
textField.addListener(SWT.KeyUp, new Listener() {
#Override
public void handleEvent(Event e) {
}
});
In the handleEvent method I set e.doit to false and then I try to differ between the arrow keys, the space key or some combinations of ALT + letter etc.
I'm wondering a bit because my application executes not the right tasks. So I debugged the coe and found out that the handleEvent method is executing three times with different event object each time.
That's not good beacause in this case it's almost impossible to make it work in the expected way - each call could replace f.e. attribute values of the previous call.
And I don't know the reason: I press one key and the handler method is called three times. Why? It's a little bit illogical for me...
+++ EDITED +++
package mytest;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
public class Main {
private static int i = 0;
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new FillLayout());
Text textField = new Text(shell, SWT.BORDER | SWT.READ_ONLY | SWT.SINGLE);
textField.addListener(SWT.KeyUp, new Listener() {
#Override
public void handleEvent(Event e) {
e.doit = false;
System.out.println("Call #" + (++i) + ": keyCode=" + e.keyCode);
}
});
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
}
You can run this little example and then press f.e. ALT+A, CTRL+I or ARROW_DOWN and you will see that the handle method is called very often per press and this is not what I'm expecting from a key listener!
Solution
What you want to observe is the other fields in the event:
stateMask:
the state of the keyboard modifier keys and mouse masks at the time
the event was generated.
keyLocation:
depending on the event, the location of key specified by the keyCode
or character. The possible values for this field are SWT.LEFT,
SWT.RIGHT, SWT.KEYPAD, or SWT.NONE representing the main keyboard
area.
The location field can be used to differentiate key events that
have the same key code and character but are generated by different
keys on the keyboard. For example, a key down event with the key code
equal to SWT.SHIFT can be generated by the left and the right shift
keys on the keyboard.
The location field can only be used to determine the location of the
key code or character in the current event. It does not include
information about the location of modifiers in the state mask.
Updated Example
The correct, expected behaviour is as each key is released you get notification. Replace your println in your question with:
System.out.println("Call #" + (++i) + ": keyCode=" + e.keyCode + " stateMask=" + e.stateMask
+ " ctrlPressed=" + ((e.stateMask & SWT.CTRL) != 0) + " shiftPressed="
+ ((e.stateMask & SWT.SHIFT) != 0)+ " altPressed="
+ ((e.stateMask & SWT.ALT) != 0));
and you should observe the correct behaviour. The stateMask details the state (e.g. control key pressed).
With your code updated with the extra prints, this is what you see in the console:
for a:
Call #11: keyCode=97 stateMask=0 ctrlPressed=false shiftPressed=false altPressed=false
For ctrl+a, with a released first:
Call #14: keyCode=97 stateMask=262144 ctrlPressed=true shiftPressed=false altPressed=false
Call #15: keyCode=262144 stateMask=262144 ctrlPressed=true shiftPressed=false altPressed=false
For ctrl+a, with ctrl released first:
Call #16: keyCode=262144 stateMask=262144 ctrlPressed=true shiftPressed=false altPressed=false
Call #17: keyCode=97 stateMask=0 ctrlPressed=false shiftPressed=false altPressed=false
For ctrl+shift+alt+a, with a released first:
Call #19: keyCode=97 stateMask=458752 ctrlPressed=true shiftPressed=true altPressed=true
Call #20: keyCode=65536 stateMask=458752 ctrlPressed=true shiftPressed=true altPressed=true
Call #21: keyCode=131072 stateMask=393216 ctrlPressed=true shiftPressed=true altPressed=false
Call #22: keyCode=262144 stateMask=262144 ctrlPressed=true shiftPressed=false altPressed=false
Why?
There are lots of places where you want to know if just the ctrl key is pressed and/or released. Consider hovering over a method or variable in Eclipse and pressing/releasing ctrl.
Update
Based on additional information provided in the comments, the OP wants to allow users to create accelerators.
jface, the layer on top of SWT has such a class already KeySequenceText (code: in git),
A wrapper around the SWT text widget that traps literal key presses
and converts them into key sequences for display. There are two types
of key strokes that are displayed: complete and incomplete. A complete
key stroke is one with a natural key, while an incomplete one has no
natural key. Incomplete key strokes are only displayed until they are
made complete or their component key presses are released.
Obviously I would recommend reusing that instead of spinning a new one. It is just shy of 1000 lines of code that is heavily in use already, so presumably well exercised and (mostly!) bug free.
Since you are using SWT outside of the Eclipse platform, if you do want to take this approach, you can use jface outside the platform too.
Usually listeners added with addListener are called very frequently at every breath almost. For key events , use
textField.addKeyListener( new KeyAdapter()
{
public void keyPressed( Event e )
} );
Related
I'm developing my own text-like JComponent. It isn't a subclass of JTextComponent, because it isn't using a Document as the model. I'd still like to support the standard mnemonics of cut/copy/paste, but I know that the keystrokes depend on the platform.
Ultimately, I'll let the user edit the keybindings themselves, but for now, I'd like to at least default to something that is sensible.
Is it possible to get it from the LookAndFeel somehow? Or do I need to detect the platform myself and just have a mapping per platform?
I'm using Java 8 if that makes a difference.
There is no LAF property that I'm aware of for this purpose.
However you might be able to use the information from the InputMap of the LAF.
The following works for Windows 8:
import java.awt.*;
import javax.swing.*;
public class PlatformMnemonics
{
public static void main(String[] args)
{
KeyStroke copyKeyStroke = null;
KeyStroke cutKeyStroke = null;
KeyStroke pasteKeyStroke = null;
InputMap im = (InputMap) UIManager.get("TextField.focusInputMap");
for (KeyStroke keyStroke: im.keys())
{
boolean upperCase = Character.isUpperCase( keyStroke.getKeyCode() );
if ( upperCase )
{
String actionMapKey = im.get( keyStroke ).toString();
if ("copy-to-clipboard".equals(actionMapKey))
copyKeyStroke = keyStroke;
else if ("cut-to-clipboard".equals(actionMapKey))
cutKeyStroke = keyStroke;
else if ("paste-from-clipboard".equals(actionMapKey))
pasteKeyStroke = keyStroke;
}
}
System.out.println("Copy KeyStroke: " + copyKeyStroke);
System.out.println("Cut KeyStroke: " + cutKeyStroke);
System.out.println("Paste KeyStroke: " + pasteKeyStroke);
}
}
Note there are actually 3 bindings on Windows for each Action as you can see in the Key Bindings programs that displays all the key bindings for every Swing component. I just displayed the binding I think you are interested in.
So, the closest I found to do what I want is the OS X Integration for Java page from Apple. There is a method on the Toolkit class getMenuShortcutKeyMask(), which will help support what I want.
For example, the following will get the right keystroke for "meta-v" on mac, and "ctrl-v" on windows/linux.
KeyStroke.getKeyStroke(KeyEvent.VK_V,
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())
I plan on writing a solver to the recently popular 2048 game
github link
I'm wondering how I could go about this without actually building the game first then solving it... My question is: Is there a way I can send key presses (e.g. 'left' 'right' 'up' and 'down' ) into a web-browser via some sort of language like java/c?
Sorry if this question has been posted before, I was not sure how to actually phrase the question and could not find any results.
use keybd_event function to send key press,
example :
keybd_event(VK_UP,0xE0,0,0);//do click, it will be stay pressed until you release it
keybd_event(VK_UP,0xE0,KEYEVENTF_KEYUP,0);//release click
the second parameter is scan code,there is a list of make and break scan codes for each key
http://stanislavs.org/helppc/make_codes.html,
and here you can find the virtual key codes
http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
Using Java applets you can add a text listener to your component and capture the Keystrokes. For example, in the code below you are capturing the keystrokes of a textbox.
import java.awt.event.*;
import java.awt.*;
import java.applet.*;
public class KeyReader extends Applet{
private static final long serialVersionUID = 1L;
public void init(){
TextField textBox = new TextField(" ");
add(textBox);
textBox.addKeyListener (new KeyAdapter() {
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
System.out.println("You Pressed " + keyCode);
}
}
);
}
}
I'm working on a JTexfield which sets its own text to the name of the key pressed when it has the focus on the window. I've managed to let it have only a word with the code:
#Override
public void keyReleased(KeyEvent ev) {
if (ev.getKeyCode() != 0) {
keyTrigger = ev.getKeyCode();
txtTrigger.setText(ev.getKeyText(keyTrigger));
}
}
#Override
public void keyTyped(KeyEvent ev) {
txtTrigger.setText("");
}
However it looks horrible when you press special keys like F1--12 or Ctrl because it keeps the last typed non-special key (for example, if you press 'T' and then 'Ctrl', the text in the field keeps being 't' until you release the 'Ctrl' key).
This is the code so far for the JTextField:
txtTrigger = new JTextField();
txtTrigger.setColumns(10);
txtTrigger.addKeyListener(this);
txtTrigger.setBounds(80, 5, 64, 20);
contentPane.add(txtTrigger);
What I want is the field to be empty until you release the key. How can I get the application working this way?
I don't think a editable text field is your best choice here. What I've done in the past is basically faked it.
I've generated a custom component that "looks" like a JTextField and, using my own KeyListener, I've added elements to the view (I did my own "key" renderer, but you could simply have a list of String elements that you can render).
Basically, when keyPressed is triggered, I would add the key code to a list (taking into consideration things like its modifier state). If another key event is triggered with the same key code, then you can ignore it.
When keyReleased is triggered, you can remove that keycode from the active list.
You can add the keylistener to the jframe or many components...
Just make sure that component has the focus.
I had asked this in the comments section of another question (> How do I handle simultaneous key presses in Java?), and was asked to make a new question altogether.
My problem is that when I create an ArrayList of keypresses they are not removed fast enough via the keyReleased event if the user holds down the keys. I want movement to be with "asdf" and North, East, South, West, NorthEast... etc.
Here is my code for both events:
#Override
public void keyPressed(KeyEvent e) {
if(chatTextField.isFocusOwner() == true){
//do nothing - don't walk
} else {
logger.debug("Key Pressed: " + e.getKeyChar());
lastKey = keysPressed.get(keysPressed.size()-1);
for (String key : keysPressed){
if (!key.contains(String.valueOf(e.getKeyChar())) && !lastKey.contains(String.valueOf(e.getKeyChar()))){
keysPressed.add(String.valueOf(e.getKeyChar()));
System.out.println("ADDED: " + keysPressed);
}
}
String keysList = keysPressed.toString();
if (keysList.contains("w")){
if (keysList.contains("d")){
requestCharacterMove("NorthEast");
} else if(keysList.contains("a")){
requestCharacterMove("NorthWest");
} else{
requestCharacterMove("North");
}
} else if (keysList.contains("s")){
if (keysList.contains("d")){
requestCharacterMove("SouthEast");
} else if(keysList.contains("a")){
requestCharacterMove("SouthWest");
} else{
requestCharacterMove("South");
}
} else if (keysList.contains("d")){
requestCharacterMove("East");
} else if (keysList.contains("a")){
requestCharacterMove("West");
}
}
}
#Override
public void keyReleased(KeyEvent e) {
if(chatTextField.isFocusOwner() == true){
//do nothing - don't walk
} else {
logger.debug("Key Released: " + e.getKeyChar());
for (String key : keysPressed){
if (key.contains(String.valueOf(e.getKeyChar()))){
keysPressed.remove(String.valueOf(e.getKeyChar()));
System.out.println("REMOVED: " + keysPressed);
}
}
}
}
#Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
Until I added the second check in there via the lastKey(String) variable the pyramid created was enormous. Even with that second check the list grows and almost always has two-three duplicates. Any help on this would be great as my character is moving awkwardly. :(
Also any way to remove duplicate conversions to char, string, arrayList would be great as I'm nervous I used too many types for something "simple".
Your obseravtion that things are handled slowly most likely is caused solely be the many System.out.println() statements.
Your problem that you do not get diagonal movement stems from your somewhat faulty checking logic - instead of explicitly checking if (for example) keys A and B are pressed, just check them independently - key A moves the character in one direction, B in another. In total (e.g.), by moving WEST and NORTH you will have effectively moved NORTHWEST.
Instead of a list of pressed keys, you could use a java.util.BitSet and just set the bit for each key that is currently pressed. That should also drastically reduce the amount of code you need to write (keyPressed just sets the bit indicated by key code, keyReleased clears it). To check if a key is pressed you ask the BitSet then if the bit for the code is currently set.
EDIT: Example of using BitSet instead of a list
public class BitKeys implements KeyListener {
private BitSet keyBits = new BitSet(256);
#Override
public void keyPressed(final KeyEvent event) {
int keyCode = event.getKeyCode();
keyBits.set(keyCode);
}
#Override
public void keyReleased(final KeyEvent event) {
int keyCode = event.getKeyCode();
keyBits.clear(keyCode);
}
#Override
public void keyTyped(final KeyEvent event) {
// don't care
}
public boolean isKeyPressed(final int keyCode) {
return keyBits.get(keyCode);
}
}
I made the example implement KeyListener, so you could even use it as is. When you need to know if a key is pressed just use isKeyPressed(). You need to decide if you prefer with raw key code (like I did) or go with key character (like you currently do). In any case, you see how using the BitSet class the amount of code for recording the keys reduces to a few lines :)
As an alternative, this game uses the numeric keypad to implement each (semi-) cardinal direction with a single keystroke. The default arrangement is shown in the Design section. The keys may be individually reassigned to map a similar rosette anywhere on the keyboard.
Looks like you are not handling threading in Java right. There are three threads (minimum) to any Java program. They are the main program thread, the event dispatch thread, and one more that i can't remember right now.
Whenever you get an event it is delivered to you by a special thread (I believe it's the event dispatch thread, but that is besides the point). You are not allowed to do anything (that takes time) on this thread, that will freeze up your input and cause you to miss events, making Java look unresponsive. So what has happened is you have broke the event system in java. What you should do is store the result in some sort of buffer, which is the fasted thing you can be expected to do with the event, then it is handled later as I will describe.
[Aside:
A funny application is to make a simple gui, and on the press of the button call wait on the thread for like 5 seconds. Your entire gui will freeze until the delay has finished!]
You should have a different thread running on the side (probably your main thread). It will run some sort of loop, which controls the frames in your program, completing once per game cycle. Once each cycle this thread reads the results stored in the input buffer and processes them. The theory behind this is simple, but the execution can be a little messy, because you will need to make sure that no input events are dropped or read more then once. Either way, good luck with your game!
I have written an SWT UI which has a primary function of displaying text in a StyledText control. I want to add a handler for Ctrl+F so that when that shortcut is pressed the focus is set to a search box. I have tried using the following code to detect the keypress.
sWindow = new Shell();
...
sWindow.getDisplay().addFilter(SWT.KeyDown, new Listener()
{
#Override
public void handleEvent(Event e)
{
System.out.println("Filter-ctrl: " + SWT.CTRL);
System.out.println("Filter-mask: " + e.stateMask);
System.out.println("Filter-char: " + e.character);
}
});
I was expecting that when I pressed Ctrl+f I would see the following output:
Filter-ctrl: 262144
Filter-mask: 262144
Filter-char: f
However, in practice I actually see the following.
Filter-ctrl: 262144
Filter-mask: 262144
Filter-char: <unprintable char - displayed as a box in eclipse console>
I have two questions:
Is Display.addFilter(...) the best way to add a global shortcut? I tried Display.addListener(...) but this didn't receive any events at all.
Why don't I get the pressed character when I'm holding down Ctrl? When I hold down alt or shift I get the expected mask and the pressed character.
Is Display.addFilter(...) the best way to add a glbal shortcut? I tried Display.addListener(...) but this didn't receive any events at all.
Yes, normally Display.addFilter(...) is the best way to add a glbal shortcut because they have higher preference over the event listeners. See the below comment from Display.addFilter(...) javadoc.
Because event filters run before other
listeners, event filters can both
block other listeners and set
arbitrary fields within an event. For
this reason, event filters are both
powerful and dangerous. They should
generally be avoided for performance,
debugging and code maintenance
reasons.
For your second question:
Why don't I get the pressed character when I'm holding down ctrl? When I hold down alt or shift I get the expected mask and the pressed character.
The problem is that you are looking at the wrong place. Instead of querying e.character you should be using e.keyCode. As per javadoc of e.character you won't get just character f:
Depending on the event, the character
represented by the key that was typed.
This is the final character that
results after all modifiers have been
applied. For example, when the user
types Ctrl+A, the character value is
0x01 (ASCII SOH).
So when you press CTRL+f then it converts in 0x06 (ASCII ACK). Which is not the case when you do ALT+f or SHIFT+f.
On the other hand the javadoc of e.keyCode says:
depending on the event, the key code
of the key that was typed, as defined
by the key code constants in class
SWT. When the character field of the
event is ambiguous, this field
contains the unaffected value of the
original character. For example,
typing Ctrl+M or Enter both result in
the character '\r' but the keyCode
field will also contain '\r' when
Enter was typed and 'm' when Ctrl+M
was typed.
Check the below code for more details. For demo I have tried to put listener on Display and Test.
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
public class ControlF
{
public static void main(String[] args)
{
Display display = new Display ();
final Shell shell = new Shell (display);
final Color green = display.getSystemColor (SWT.COLOR_GREEN);
final Color orig = shell.getBackground();
display.addFilter(SWT.KeyDown, new Listener() {
public void handleEvent(Event e) {
if(((e.stateMask & SWT.CTRL) == SWT.CTRL) && (e.keyCode == 'f'))
{
System.out.println("From Display I am the Key down !!" + e.keyCode);
}
}
});
shell.addKeyListener(new KeyListener() {
public void keyReleased(KeyEvent e) {
if(((e.stateMask & SWT.CTRL) == SWT.CTRL) && (e.keyCode == 'f'))
{
shell.setBackground(orig);
System.out.println("Key up !!");
}
}
public void keyPressed(KeyEvent e) {
if(((e.stateMask & SWT.CTRL) == SWT.CTRL) && (e.keyCode == 'f'))
{
shell.setBackground(green);
System.out.println("Key down !!");
}
}
});
shell.setSize (200, 200);
shell.open ();
while (!shell.isDisposed()) {
if (!display.readAndDispatch ()) display.sleep ();
}
display.dispose ();
}
}