JDK-4131761 : What is the correct focusing behavior
  • Type: Enhancement
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 1.1.6
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_nt
  • CPU: x86
  • Submitted: 1998-04-22
  • Updated: 2000-10-04
  • Resolved: 2000-10-04
Related Reports
Duplicate :  
Relates :  
Description
A group of Focus issues.
----------------------------------------------------

The applet consists of two simple lightweight components which are KeyListeners (these are prototypical lightweight InputField objects). When the applet is started via appletviewer, the first object to have focus is NOT the applet, but rather: sun.applet.AppletViewerPanel.  We must explicitly call requestFocus() in the init() method of the applet to get focus onto it.

Key presses are recorded by the applet's KeyListener at this point, but not by the two simple lightweight components.  This is correct behavior.

Click off of the applet to an outside application. You will notice on the console that the applet loses the focus.  Again, this is correct behavior.  Click back on the applet, being careful not to click on either of the lightweight components.  You will notice on the console that it is not the applet that regains focus (which as I understand it, is what should happen) but rather: sun.applet.AppletViewer. Interestingly enough, this is not the object which initially has focus, which is not sun.applet.AppletViewer, but rather sun.applet.AppletViewerPanel.  Anyway, our "FocusTransfer" object listens for focus on the AppletViewer object and correctly transfers the focus back to the applet.  This can be verified on the console by appropriate messages.  THIS IS A WORKAROUND.

Next, click on one of the two lightweight components. It will get focus.  A message is printed on the console to this effect.  It will now get key press events. HOWEVER, no object lost focus - I would expect the applet to.  In addition, the applet's own KeyListener continues to get key events.  It seems that the applet and the lightweight component have focus at the same time.  Is this the correct behavior?  I am unsure.

Switching between the two lightweights (by clicking) seems to behave correctly, although the applet continues to have focus at this time.

Finally, while one of the lightweight components has the focus, click on an outside application to remove the focus, then click back on the applet.  According to the console output, AppletViewer once again gets the focus, although I believe the correct behavior should be that the last component to have focus (the lightweight) should get it again.  Interestingly enough, our FocusTransfer object hears the FocusEvent on the AppletViewer and reacts.  However, although it's response is to call requestFocus() on the applet, it is the lightweight component that gets focus instead!  I am unsure as to why this occurs.

----------------------------------------------------

To see this applet, use the following HTML code:
  <APPLET CODE="InputFocusBug.class" WIDTH=430 HEIGHT=270></APPLET>
 
  This has been tested with the JDK 1.1.6M appletviewer on
  Windows NT Server 4.0 on a Pentium Pro 180 with 64 megabytes.



------------------- Code -------------------


import java.applet.Applet;
import java.awt.event.FocusListener;
import java.awt.event.KeyListener;
import java.awt.Color;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.awt.Graphics;
import java.awt.Component;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;

// This is the main applet to demonstrate the problem.
public class InputFocusBug extends Applet implements FocusListener, KeyListener
{
    SimpleKeyInput inputOne;
    SimpleKeyInput inputTwo;
    boolean inFocus = false;
    char lastKey = ' ';

    public void init( )
    {
        // basic setup
        setLayout( null);
        setBackground( Color.white);
        setForeground( Color.black);
        setSize( 430, 270);

        // add the focus and key listeners
        addFocusListener( this);
    	addKeyListener( this);

        // add the "simple input"s to the applet
        inputOne = new SimpleKeyInput( );
        inputTwo = new SimpleKeyInput( );
        inputOne.setBounds( 36, 56, 144, 24);
        inputTwo.setBounds( 36, 86, 144, 24);
        add( inputOne);
        add( inputTwo);

        // make sure we're showing everything!
        validate( );
        repaint( );

        // get the focus to start off
        requestFocus( );

        // This is an attempt to make sure whenever part of
        // appletviewer gets the focus (either the AppletViewer
        // itself, or the AppletViewerPanel), we switch the
        // focus to the applet again.
        FocusTransfer theTransferObject = new FocusTransfer( this);
        getParent( ).addFocusListener( theTransferObject);
        getParent( ).getParent( ).addFocusListener( theTransferObject);
    }

    // set our internal state to know we've got the focus, then repaint
    public void focusGained( FocusEvent evt)
    {
        System.out.println( evt.getSource( ) + " got focus.");
        System.out.flush( );
        inFocus = true;
        repaint( );
    }

    // set our internal state to know we've lost the focus, then repaint
    public void focusLost( FocusEvent evt)
    {
        System.out.println( evt.getSource( ) + " lost focus.");
        System.out.flush( );
        inFocus = false;
        repaint( );
    }

    // set our internal record of the key that was typed, then repaint
    public void keyTyped( KeyEvent evt)
    {
        lastKey = evt.getKeyChar( );
        repaint( );
    }

    // these two methods aren't used
    public void keyPressed( KeyEvent evt) {}
    public void keyReleased( KeyEvent evt) {}

    public synchronized void update( Graphics g)
    {
        paint( g);
    }

    public void paint( Graphics g)
    {
        // paint the background
        g.setColor( getBackground( ));
        g.fillRect( 0, 0, 430, 270);

        // paint what the last key typed was
        g.setColor( getForeground( ));
        g.drawString( "Last Key: " + lastKey, 20, 150);

        // paint whether or not we have focus
        if( inFocus)
        {
            g.drawString( "Applet Has Focus", 20, 170);
        }
        else
        {
            g.drawString( "Applet Does Not Have Focus", 20, 170);
        }

        // paint the inputs
        super.paint( g);
    }
}

// This object will switch the focus to another object
// FOCUSTRANSFER is a WORKAROUND
class FocusTransfer implements FocusListener
{
    // The object we want to switch focus to
    Component component = null;

    // The constructor, setting up the target object
    public FocusTransfer( Component component)
    {
        this.component = component;
    }

    // when we hear a focus message, automatically request
    // focus on the target object after printing out a
    // status message
    public void focusGained( FocusEvent evt)
    {
        System.out.println( evt.getSource( ) + " got focus.");
        System.out.flush( );
        System.out.println( "Calling requestFocus() on " + component);
        System.out.flush( );
        component.requestFocus( );
    }

    // merely print out a status message when whatever we
    // are listening to loses focus
    public void focusLost( FocusEvent evt)
    {
        System.out.println( evt.getSource( ) + " lost focus.");
        System.out.flush( );
    }
}

// This is out simple input class
class SimpleKeyInput extends Component implements MouseListener, KeyListener, FocusListener
{
    char lastKey = ' ';
    boolean inFocus = false;

    public SimpleKeyInput( )
    {
        // should get keyboard input
        addKeyListener( this);

        // get ready to listen for clicks
        addMouseListener( this);

        // make sure we know when we get and lose focus
        addFocusListener( this);
    }

    // these methods are not used
    public void mouseClicked( MouseEvent e) {}
    public void mouseEntered( MouseEvent e) {}
    public void mouseExited( MouseEvent e) {}
    public void mousePressed( MouseEvent e) {}
    public void keyPressed( KeyEvent evt) {}
    public void keyReleased( KeyEvent evt) {}

    // set our internal state to know we've got the focus, then repaint
    public void focusGained( FocusEvent evt)
    {
        System.out.println( evt.getSource( ) + " got focus.");
        System.out.flush( );
        inFocus = true;
        repaint( );
    }

    // set our internal state to know we've lost the focus, then repaint
    public void focusLost( FocusEvent evt)
    {
        System.out.println( evt.getSource( ) + " lost focus.");
        System.out.flush( );
        inFocus = false;
        repaint( );
    }

    // set our internal record of the key that was typed, then repaint
    public void keyTyped( KeyEvent evt)
    {
        lastKey = evt.getKeyChar( );
        repaint( );
    }

    // if the button was clicked, let our listeners know
    public void mouseReleased( MouseEvent e)
    {
        requestFocus( );
    }

    // paint the simplekeylistener
    public void paint( Graphics g)
    {
        g.setColor( getBackground( ));
        g.fillRect( 0, 0, getSize( ).width, getSize( ).height);
        g.setColor( getForeground( ));
        if( inFocus)
        {
            g.drawString( "FOCUSED: " + lastKey, 5, getSize( ).height - 3);
        }
        else
        {
            g.drawString( "UNFOCUSED: " + lastKey, 5, getSize( ).height - 3);
        }
        g.drawRect( 0, 0, getSize( ).width - 1, getSize( ).height - 1);
    }
}


Comments
EVALUATION Currently planning to implement this functionality, or equivalent functionality, in the merlin focus enhancements. david.mendenhall@eng 1999-12-14
14-12-1999