Name: pa48320 Date: 11/29/2001
(this bug is to track the JInitiator Replacement Project. please direct
any questions to Peter Allenbach. Thank you)
Implicit focus transfers in the AWT classes appear to combine
with the new focus model in JDK 1.4 to cause several bugs
encountered during the certification of Oracle Applications
11i on the JDK 1.4 platform.
Even if complete workarounds for these issues prove to be
feasible, it looks like they will fall under the category of
rearchitecting rather than bug fixing, so we would very much
like to search for solutions to better preserve compatibility
between 1.1.8 and 1.4.
There are a set of "focus sensitive" actions the JDK allows on
UI components, which are sensitive in the sense that if the
component on which we?re performing them currently has focus,
they implicitly generate focus changes. Examples of these
behaviors include disabling, hiding, or removing a focused
component from its container, all of which will cause the JDK to
request focus on some other component.
Performing these actions on components which currently have focus
is a requirement for us because we are a tool which allows
application developers to declaratively design a UI and set of
behaviors the UI should have in response to user events. For
example, within our model it is a completely valid UI design
to respond to a user mouse click by disabling the button which
has focus, just so long as focus is set on some other component.
When it is time for us to actually implement this sort of
sensitive action on a component at the java level, the question
becomes how we can use java to accomplish that action and ensure
focus ends up in the correct component, all within our event
listener where we process the user action.
In the context of JDK 1.1.8, our solution was to set focus to
some other component (the ultimate recipient if it is already
a valid focus recipient, or some temporary component otherwise)
before performing the sensitive action. Since focus requests
were resolved synchronously, this would avoid any focus transfers
on the part of the JDK since by the time we performed the sensitive
action, that target component was not currently the focus owner.
We could then request focus to the correct component at the end
of our processing, and all of this could happen in one block of
code within a single event listener method.
In JDK 1.4 this strategy no longer works, since requesting focus
to another component no longer prevents the JDK from transferring
focus, and because the order of focus requests is (1) Forms
followed by (2) the JDK, focus will end up in the wrong component
once both requests are resolved.
The FocusTest testcase:
-----------------------
The FocusTest testcase contains a Frame with a Button and two
TextFields. The Button starts with the focus, and when the user
clicks it the application is designed to disable the button and
set focus to the second TextField (TextField2). This demonstrates
the issue described above.
Our JDK 1.1.8 strategy for this is to requestFocus on the desired
component (TextField2) and then disable the Button. Since focus
requests are processed synchronously in 1.1.8, the sequence of
events are as follows:
1. TextField2 requests focus
2. TextField2 receives focus
3. Button calls setEnabled(false)
4. Button becomes disabled
If that same code is run in JDK 1.4 where focus requests happen
asynchronously, the sequence of events is a little bit more
complex, and runs as follows:
1. TextField2 requests focus
2. TextField2?s focus request (1) is queued
3. Button calls setEnabled(false)
4. _autoTransferFocus requests focus on TextField1 (the next
focusable component)
5. TextField1?s focus request (2) is queued
6. Button becomes disabled
7. The first focus request (1) is processed and TextField2
receives focus
8. The second focus request (2) is processed and TextField1
receives focus
[FocusTest.java]
----------------
import java.awt.*;
import java.awt.event.*;
public class FocusTest extends Frame implements ActionListener
{
Panel panel = new Panel();
MyButton button1 = new MyButton("Push!");
MyTextField field1 = new MyTextField(" Field 1");
MyTextField field2 = new MyTextField(" Field 2");
FocusTest()
{
add(panel);
addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0); } } );
button1.addActionListener(this);
panel.add(button1);
panel.add(field1);
panel.add(field2);
pack();
}
public void actionPerformed(ActionEvent event)
{
if ( event.getSource() == button1 )
{
field2.requestFocus();
button1.setEnabled(false);
}
}
static public void main(String[] args)
{
(new FocusTest()).setVisible(true);
}
}
class MyTextField extends TextField implements FocusListener
{
MyTextField(String s)
{
super(s);
addFocusListener(this);
}
public void requestFocus()
{
System.out.println("[MyTextField] requestFocus() from "+
this.getText());
super.requestFocus();
}
public void focusGained(FocusEvent e)
{
System.out.println("[MyTextField] focusGained on " +
((MyTextField)(e.getComponent())).getText());
}
public void focusLost(FocusEvent e)
{
System.out.println("[MyTextField] focusLost on " +
((MyTextField)(e.getComponent())).getText());
}
}
class MyButton extends Button implements FocusListener
{
MyButton(String s)
{
super(s);
addFocusListener(this);
}
public void requestFocus()
{
System.out.println("[MyButton] requestFocus() from " +
this.getLabel());
super.requestFocus();
}
public void focusGained(FocusEvent e)
{
System.out.println("[MyButton] focusGained on " +
((MyButton)(e.getComponent())).getLabel());
}
public void focusLost(FocusEvent e)
{
System.out.println("[MyButton] focusLost on " +
((MyButton)(e.getComponent())).getLabel());
}
}
(Review ID: 135397)
======================================================================