JDK-5089436 : REGRESSION: requestFocusInWindow() fails for comp. on JTabbedPane aftertab switch
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 5.0,6
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic,solaris_2.5.1,windows_xp
  • CPU: generic,x86
  • Submitted: 2004-08-19
  • Updated: 2017-05-19
  • Resolved: 2006-04-26
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6
6 b82Fixed
Related Reports
Duplicate :  
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Relates :  
Description
Name: rmT116609			Date: 08/19/2004


FULL PRODUCT VERSION :
java version "1.5.0-beta3"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta3-b60)
Java HotSpot(TM) Client VM (build 1.5.0-beta3-b60, mixed mode, sharing)


ADDITIONAL OS VERSION INFORMATION :
Linux 2.6.4 #1 i686 Intel(R) Pentium(R) 4 CPU 2.40GHz GenuineIntel GNU/Linux

Microsoft Windows XP [Version 6.1.2600]

A DESCRIPTION OF THE PROBLEM :
Calls to JComponent.requestFocusInWindow() return false and have no
effect for components in a JTabbedPane when carried out straight after
a tab switch.

If carried out later (e.g during processing of a subsequent event) they
work as expected.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile and execute the attached sample. Allow to run until
"Adding change listener" is printed. Try switching between the
two tabs using the mouse.

The code illustrates four things at five second intervals:

1) Selecting a tab and immediatly attempting to focus on a component
2) Selecting a tab
3) Focusing on a component on that tab
4) Adding a ChangeListener to attempt to focus on the lower text field
each time the user switches tabs
 
All focus requests succeed with JDK 1.4.1/1.4.2 and all but (3) return false
under JDK 1.5.0, build 60.


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -

Expected output would be as produced by version 1.4.1 (below).
On clicking between the tabs at the end focus stays in the bottom
two text fields.

$ java TabbedPaneDemo
Combined tab select and set focus
Focusing on 22 - requestFocusInWindow() returned true
Separate tab select
Seperate focus adjustment
Focusing on 12 - requestFocusInWindow() returned true
Adding change listener
Focusing on 22 - requestFocusInWindow() returned true
Focusing on 12 - requestFocusInWindow() returned true
Focusing on 22 - requestFocusInWindow() returned true
Focusing on 12 - requestFocusInWindow() returned true

ACTUAL -

Output from 1.5.0 is as below. On clicking between the tabs
at the end focus stays in the top two text fields.

$ java TabbedPaneDemo
Combined tab select and set focus
Focusing on 22 - requestFocusInWindow() returned false
Separate tab select
Seperate focus adjustment
Focusing on 12 - requestFocusInWindow() returned true
Adding change listener
Focusing on 22 - requestFocusInWindow() returned false
Focusing on 12 - requestFocusInWindow() returned false
Focusing on 22 - requestFocusInWindow() returned false
Focusing on 12 - requestFocusInWindow() returned false


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------

/*
 * Based on TabbedPaneDemo.java from Sun Java tutorial
 */

import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;

public class TabbedPaneDemo extends JPanel implements ChangeListener {

    JTextField  textField11;
    JTextField  textField12;
    JTextField  textField21;
    JTextField  textField22;
    JTabbedPane tabbedPane;

    public TabbedPaneDemo() {
        super(new GridLayout(1, 1));
        tabbedPane = new JTabbedPane();
        JComponent panel1 = makeTextPanel1();
        tabbedPane.addTab("Tab 1", null, panel1, "");
        JComponent panel2 = makeTextPanel2();
        tabbedPane.addTab("Tab 2", null, panel2, "");
        add(tabbedPane);

        actionThread.start();
    }

    protected JComponent makeTextPanel1() {
        JPanel panel = new JPanel(false);
        textField11 = new JTextField("11: test text one two three four");
        textField12 = new JTextField("12: example text");
        panel.setLayout(new GridLayout(2, 1));
        panel.add(textField11);
        panel.add(textField12);
        return panel;
    }

    protected JComponent makeTextPanel2() {
        JPanel panel = new JPanel(false);
        textField21 = new JTextField("21: example text");
        textField22 = new JTextField("22: example text");
        panel.setLayout(new GridLayout(2, 1));
        panel.add(textField21);
        panel.add(textField22);
        return panel;
    }

    private static void createAndShowGUI() {
        JFrame frame = new JFrame("TabbedPaneDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(new TabbedPaneDemo(), BorderLayout.CENTER);
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });

    }

    public void stateChanged(ChangeEvent e) {
        attemptToSetFocus();
    }

    private void attemptToSetFocus() {
        boolean result;
        switch ( tabbedPane.getSelectedIndex() ) {
        case 0:
            System.out.print("Focusing on 12 - ");
            result = textField12.requestFocusInWindow();
            break;
        case 1:
            System.out.print("Focusing on 22 - ");
            result = textField22.requestFocusInWindow();
            break;
        default:
            throw new Error("Unexpected index value");
        }

        System.out.println("requestFocusInWindow() returned " + result);
    }

    Thread actionThread = new Thread() {
        public void run() {
            try { Thread.sleep(5000); } catch (InterruptedException e) {}

            System.out.println("Combined tab select and set focus");

            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    tabbedPane.setSelectedIndex(1);
                    attemptToSetFocus();
                }
            });

            try { Thread.sleep(5000); } catch (InterruptedException e) {}

            System.out.println("Separate tab select");

            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    tabbedPane.setSelectedIndex(0);
                }
            });

            try { Thread.sleep(5000); } catch (InterruptedException e) {}

            System.out.println("Seperate focus adjustment");

            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    attemptToSetFocus();
                }
            });

            try { Thread.sleep(5000); } catch (InterruptedException e) {}

            System.out.println("Adding change listener");
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    tabbedPane.addChangeListener(TabbedPaneDemo.this);
                }
            });
        }
    };

}


---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :

Start a thread and which calls SwingUtilities.invokeLater()
to call to requestFocusInWindow().

Release Regression From : 1.4.2_05
The above release value was the last known release where this 
bug was known to work. Since then there has been a regression.

(Incident Review ID: 297637) 
======================================================================

Comments
EVALUATION Contribution-Forum:https://jdk-collaboration.dev.java.net/servlets/ProjectForumMessageView?messageID=12461&forumID=1463
05-04-2006

EVALUATION I've arrived at a solution for this problem: All changes to JTabbedPane's selection, end up going through the method fireStateChanged(). As long as developers have added their stateChanged listeners to the tabbed pane itself (and not the model), we can use fireStateChanged() to process the selection/visibility change immediately. And that's what I've done. The logic for changing component visibility, normally processed during layout in BasicTabbedPaneUI, has been duplicated in fireStateChanged(). As such, visibility changes will happen immediately, thereby allowing developers to once again change focus from their listeners.
03-04-2006

CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: dragon mustang
25-09-2004

EVALUATION This bug is a result of a backward incompatible change in AWT. AWT documentation for requestFocusInWindow clearly says that the component must be "displayable", "visible", and "focusable" for the focus request to be granted. The component in question here has been determined to be all three. The component is simply not showing() because it's parent is not yet visible - that comes later after re-layout of the tabbed pane. Previously, this was not a limitation of requestFocusInWindow(). The change was made in 1.5.0 - see requestFocusHelper in Component.java. This is a regression and is counter to the current specification. In 1.4.2, parent visibility had nothing to do with granting a requestFocusInWindow() request. ###@###.### 2004-08-19 Unfortunately, there is little we can do. To be able to receive focus, the component must be focusable, displayable and recursively visible up-to(but not necessary including) the top-level window. The documentation for requestFocusInWindow and requestFocus is not complete. Note that there is no requirement for isShowing() of the Component to return true - it is possible to request focus for a Component in invisible toplevel, we have most-recent focus owner/default focus component mechanism to complete such requests. However, there is no mechanisms to complete requests in invisible containers. The only way to handle request for invisible heirarchies is when it is top-level which is invisible. In that case, when it becomes visible, we can get most-recent focus owner and request focus to it. If at the same time, one of the parent of this component is invisible and we allowed such request, we won't be able to request focus or it will arrive incorectly, to the component which doesn't exist on the screen. We can not allow request assuming that later Container becomes visible. If you need to make such request, first make Container visible, then make the request. ###@###.### 2004-08-20 I disagree with the previous evaluation. The evaluation states that there is no mechanism to handle request for invisible containers. But that is exactly what the focus handling did in 1.4.2. The evaluation also states that this could allow focus to arrive incorrectly at an invisible container. I understand that this isn't ideal but, again, it's the way it worked in 1.4.2. This change is backward-incompatible and has broken Swing. It could break others too. If you need to enforce not setting focus when a parent is invisible, please handle it in the traversal policies, not by breaking clearly spec'd API. Thanks! ###@###.### 2004-08-20 It worked in some cases, when races allowed the components to receive focus since after the request people eventually were making those Containers visible. However, it might not have worked, and that was the essense of the fix for which introduced this change. The result was that it focus was given to the invisible component and there was no way for the user to tell where the focus is. Also, native code was never able to set focus to invisible component, so this DID NOT work even in 1.4.2. This result is much more damaging than unability to set focus to the not-applicable component, in this case at least some component is focusable, and either by mouse or keyboard user can transfer focus to the required component(if it becomes visible). For Swing, the workaround in this case is trivial and Swing must be changed to implement it. ###@###.### 2004-08-20 I have created CCC for this, see http://ccc.sfbay/5089436 ###@###.### 2004-08-20 Swing and AWT have discussed this. Swing will attempt to address this in our JTabbedPane code. As a fallback, AWT has said that they can restore the old behavior for Swing only if necessary. For now, we'll try without it. As for the documentation change that's needed, I've filed bug 5091908. Please re-target the CCC request at that bug. ###@###.### 2004-08-25 5092589 is waiting on this fix. ###@###.### 2004-08-26
25-08-2004

WORK AROUND From the description: "... Calls to JComponent.requestFocusInWindow() return false and have no effect for components in a JTabbedPane when carried out straight after a tab switch. If carried out later (e.g during processing of a subsequent event) they work as expected..." ###@###.### 2004-08-24
24-08-2004

SUGGESTED FIX I suggest we modify the documentation for requestFocus* methods of Component: change "This component must be displayable, visible, and focusable for the request to be granted" to "This component must be displayable, focusable, visible and all parent Containers not including top-level Window must be visible, for the request to be granted" ###@###.### 2004-08-20 I disagree with the suggested fix. I suggest we restore the behavior to what it was before this change in Tiger. This matches the current spec. ###@###.### 2004-08-20
20-08-2004