|
Quick Lists
|
|
Bug ID:
|
4150021
|
|
Votes
|
43
|
|
Synopsis
|
Frame cannot handle keyboard events (keyTyped) if buttons are added to frame.
|
|
Category
|
java:classes_awt
|
|
Reported Against
|
1.1.3
, merlin-beta
, merlin-beta2
|
|
Release Fixed
|
1.4.2(mantis)
|
|
State
|
10-Fix Delivered,
bug
|
|
Priority:
|
4-Low
|
|
Related Bugs
|
4027897
,
4033894
,
4478706
|
|
Submit Date
|
17-JUN-1998
|
|
Description
|
Steps to recreate problem.
This is a simple program that changes the
backroung color of the frame. You have two
options; yellow and blue.
Press 'y' for yellow and 'b' for blue.
Nothing happens. If you click the buttons with
the mouse, everythings works as expected. It
appears that the frame can never regain focus to
accept key event(as defined in Core Java 1.1
Foundations). I tried to get the focus for the
frame by using the requestFocus() method, but it
does nothing(which I noticed was already reported).
NOTE: We are going through e-mail updates. If
the provided e-mail is return, please send all
future correspondence to xxxxx@xxxxx .
Thank you.
Here is the code:
import java.awt.*;
import java.awt.event.*;
public class ButtonTest extends Frame
{
public ButtonTest()
{
addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{System.exit(0);}
} );
setSize(300, 200);
setTitle("Button Test");
setLayout(new FlowLayout());
//Create button, add event handler, and add to frame
Button yellow = new Button("Yellow");
yellow.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{setBackground(Color.yellow);}
} );
add(yellow);
//Create button, add event handler, and add to frame
Button blue = new Button("Blue");
blue.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{setBackground(Color.blue);}
} );
add(blue);
//I can never pass through this callback,
//the frame never seems to have focus
addKeyListener(new KeyAdapter()
{
public void keyTyped(KeyEvent e)
{ ButtonTest.this.keyTyped(e); }
} );
//Tried using this method to gain focus for frame
//but it does not work.
requestFocus();
}
public void keyTyped(KeyEvent evt)
{
char keyChar = Character.toLowerCase(evt.getKeyChar());
if (keyChar == 'y') setBackground(Color.yellow);
if (keyChar == 'b') setBackground(Color.blue);
}
public static void main(String args[])
{
Frame f = new ButtonTest();
f.show();
}
}
(Review ID: 25346)
======================================================================
|
|
Work Around
|
It appears that when the buttons are added to the
frame that the buttons maintain the focus. Meaning
if there are buttons on the frame, the frame will
not accept keyboard events.
If the buttons are not installed everything works
just fine. The only way I could get the key events
to work was to add a key listner to each of the
buttons. That way, no matter which button had focus
they each would process a key event. The code I
had to add to make this work is as follows and belongs
in the constructor.
yellow.addKeyListener(new KeyAdapter()
{
public void keyTyped(KeyEvent e)
{ButtonTest.this.keyTyped(e);}
}
blue.addKeyListener(new KeyAdapter()
{
public void keyTyped(KeyEvent e)
{ButtonTest.this.keyTyped(e);}
}
Now this works, but I do not think this is the way
it should work. I would think that the button
component should not have to handle all key events.
The frame component should be able to process key
events whether or not it has buttons.
Thank you.
xxxxx@xxxxx 1998-06-17
The test program will work if keyPressed is used instead of keyTyped.
41,42c41,42
< public void keyTyped(KeyEvent e)
< { ButtonTest.this.keyTyped(e); }
---
> public void keyPressed(KeyEvent e)
> { ButtonTest.this.keyPressed(e); }
50c50
< public void keyTyped(KeyEvent evt)
---
> public void keyPressed(KeyEvent evt)
======================================================================
We can provide two workarounds for this problem:
1. invoke frame.requestFocus() on EDT by invokeAndWait()/invokeLater().
2. make the frame the initial component by changing FocusTraversalPolicy
or by calling setFocusable(true) on the frame.
xxxxx@xxxxx 21 Dec 2001
======================================================================
|
|
Evaluation
|
xxxxx@xxxxx 1998-06-18
This bug sounds as though it has the same root cause as 4033894
and/or 4027897. The problem in each case seems to be that the
frame cannot regain focus after it has lost it.
commit to hopper and tiger
======================================================================
In the test user tries to listen KeyEvents on Frame. For
this reason KeyListener is registered on the frame, and
requestFocus() is called on the frame.
But requestFocus() is called before frame showing, and AWT rejects
this call because the frame is not visible. Thus after application
start focus is placed on button (initial component), not on the frame
itself.
To fix this problem we need to call requestFocus() after
setVisible(true). But here we have xxxxx problem:
requestFocus() is called on main thread, and at the same time on EDT
DefaultKeyboardFocusManager dispatches WINDOW_GAINED_FOCUS (which
comes as a result of setVisible(true)) and in this
handler it determines component which should be a focus owner and
requests focus on this component. To find out this component, DKFM
call getMostRecentFocusOwner() method on focused window.
Thus we have the following situation:
1. On EDT we call Window.getMostRecentFocusOwner(), which returns
the button as new focus owner, since it is initial component for this
frame.
2. On main thread we call requestFocus() on the frame.
3. On EDT we call requstFocus() on the button.
And after that the button becomes a focus owner.
So, user expects the frame to be a focus owner and we try to do so
(for this reason we have most recent focus owner, which we update in
requestFocus()). But because of the thread race and the fact that our
code is not thread safe, focus is placed on the button.
xxxxx@xxxxx 21 Dec 2001
======================================================================
======================================================================
|
|
Comments
|
Submitted On 11-DEC-1998
mleflar
I had a similar situation and found that it works as expected in the win95
environment (jdk1.1.x) but on the Solaris systems it did not...
Submitted On 07-NOV-1999
koester
Actually I've encountered this situation myself, though I am not convinced it
is a bug.
The following code demonstrates a solution to the sequence of actions you
desire:
(JDK 1.1.8)
import java.awt.*;
import java.awt.event.*;
public class test extends Frame
{
public test()
{
add(new myButton());
addWindowListener(new OnWindow());
addKeyListener(new OnKey());
setSize(100, 100);
setVisible(true);
}
private class myButton extends Button
{
public myButton()
{
super("Click me");
addActionListener(new OnClick());
}
private class OnClick implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
System.out.println("Insde button handler (requesting frame
focus)");
test.this.requestFocus();
}
}
}
private class OnKey extends KeyAdapter
{
public void keyTyped(KeyEvent e)
{
System.out.println("Inside key handler");
}
}
private class OnWindow extends WindowAdapter
{
public void windowActivated(WindowEvent e)
{
System.out.println("Inside window handler (requesting frame
focus)");
requestFocus();
}
public void windowClosing(WindowEvent e)
{
setVisible(false);
dispose();
System.exit(0);
}
}
public static void main(String args[])
{
new test();
}
}
Submitted On 20-MAR-2002
soyke
These workarounds don't help much if you are trying to
capture a key event from anywhere on a screen. For
instance, we are trying to listen for an F5 key to refresh
a list no matter where the user (focus) is on a certain
screen. Adding a listener to 50+ objects on each screen is
out of the question. I tried using the AWTEventListener,
but that listens to events system-wide, thus forcing us to
determine in each eventDispatched() method if the event
happened in an object on the particular screen we're
interested in.
Submitted On 31-MAR-2002
phenix61
I agree totally with Soyke. I am trying to detect a series
of keyEvents that are done anywhere on the screen and
because I do not have access to all the code, I cannot
place a KeyListener on every object.
Submitted On 08-APR-2002
dantams
Just found the method registerKeyboardAction() in
JComponent, which takes care of just the mentioned problem.
Submitted On 08-APR-2002
dantams
Here is one workaround from the forum:
>Re: ctrl-key shortcut
>Author: Kleopatra
>In Reply To: ctrl-key shortcut Apr 8, 2002 12:07 AM
>Reply 2 of 2
>Forget about keyEvents/-Listeners - they are way too low
>level in all except very special situations when using
>Swing. Use the keybindings mechanism instead: put the key
>into the buttons inputMap of type WEN_IN_FOCUSED_WINDOW and
>the action into its actionMap.
>
>Greetings
>Jeanette
Only caveat is that it only works with Java 1.3 and upwards.
Submitted On 08-APR-2002
dantams
Just use the AWTEventListener method and then check with
((Component) Event.getSource()).getRootPane() if it's in the
right window.
It's a bit tedious and low-level, but it works.
Submitted On 13-JUN-2002
mushina
I had written some code where I added KeyEventListeners toa
a JFrame and the KeyEventListeners stopped working after a
JButton was clicked (even if no listeners were attatched to
the button). According to isFocused(), my frame still had
focus, but the KeyEventListeners were not being triggered.
I was, however able to create a workaround by using a
KeyEventDispatcher. The KeyEventDispatcher gets called
before the key gets passed to the component in focus. This
allows you to do whatever you want with the key press,
regardless of which element is in focus. The relevant code
looks something like this:
KeyboardFocusManager manager =
KeyboardFocusManager.getCurrentKeyboardFocusManager();
manager.addKeyEventDispatcher(keyDispatcher);
private KeyEventDispatcher keyDispatcher = new
KeyEventDispatcher(){
public boolean dispatchKeyEvent(KeyEvent k) {
if (key.getID() != KeyEvent.KEY_TYPED) { return false; }
/* Returning false tells the focus manager you did not do
anything with the key stroke and that it still needs to
be dealt with */
... // Do whatever you want to do with the KeyEvent
return true;
/* Returning true tells the focus manager it should no
longer worry about this keyEvent*/
};
Personally, I think that the main problem here is that the
new Focus Manager in 1.4 does not propogate KeyEvents up to
the Focused Window - it just sends them to the Focused
Component and that's it.
PLEASE NOTE: JDK6 is formerly known as Project Mustang
|
|
|
 |