4685768: A11y issue - Focus set to disabled component, can't Tab/Shift-Tab
Summary: The restore-focus procedure should skip disabled components.
Reviewed-by: art, dcherepanov
--- a/jdk/src/share/classes/java/awt/Component.java Wed Jun 11 01:31:42 2008 -0700
+++ b/jdk/src/share/classes/java/awt/Component.java Tue Jun 17 13:37:28 2008 +0400
@@ -7488,9 +7488,7 @@
Container rootAncestor = getTraversalRoot();
Component comp = this;
while (rootAncestor != null &&
- !(rootAncestor.isShowing() &&
- rootAncestor.isFocusable() &&
- rootAncestor.isEnabled()))
+ !(rootAncestor.isShowing() && rootAncestor.canBeFocusOwner()))
{
comp = rootAncestor;
rootAncestor = comp.getFocusCycleRootAncestor();
@@ -7539,9 +7537,7 @@
Container rootAncestor = getTraversalRoot();
Component comp = this;
while (rootAncestor != null &&
- !(rootAncestor.isShowing() &&
- rootAncestor.isFocusable() &&
- rootAncestor.isEnabled()))
+ !(rootAncestor.isShowing() && rootAncestor.canBeFocusOwner()))
{
comp = rootAncestor;
rootAncestor = comp.getFocusCycleRootAncestor();
@@ -8518,6 +8514,14 @@
setComponentOrientation(orientation);
}
+ final boolean canBeFocusOwner() {
+ // It is enabled, visible, focusable.
+ if (isEnabled() && isDisplayable() && isVisible() && isFocusable()) {
+ return true;
+ }
+ return false;
+ }
+
/**
* Checks that this component meets the prerequesites to be focus owner:
* - it is enabled, visible, focusable
@@ -8527,9 +8531,9 @@
* this component as focus owner
* @since 1.5
*/
- final boolean canBeFocusOwner() {
+ final boolean canBeFocusOwnerRecursively() {
// - it is enabled, visible, focusable
- if (!(isEnabled() && isDisplayable() && isVisible() && isFocusable())) {
+ if (!canBeFocusOwner()) {
return false;
}
--- a/jdk/src/share/classes/java/awt/Container.java Wed Jun 11 01:31:42 2008 -0700
+++ b/jdk/src/share/classes/java/awt/Container.java Tue Jun 17 13:37:28 2008 +0400
@@ -860,11 +860,11 @@
// If component is focus owner or parent container of focus owner check that after reparenting
// focus owner moved out if new container prohibit this kind of focus owner.
- if (comp.isFocusOwner() && !comp.canBeFocusOwner()) {
+ if (comp.isFocusOwner() && !comp.canBeFocusOwnerRecursively()) {
comp.transferFocus();
} else if (comp instanceof Container) {
Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
- if (focusOwner != null && isParentOf(focusOwner) && !focusOwner.canBeFocusOwner()) {
+ if (focusOwner != null && isParentOf(focusOwner) && !focusOwner.canBeFocusOwnerRecursively()) {
focusOwner.transferFocus();
}
}
--- a/jdk/src/share/classes/java/awt/ContainerOrderFocusTraversalPolicy.java Wed Jun 11 01:31:42 2008 -0700
+++ b/jdk/src/share/classes/java/awt/ContainerOrderFocusTraversalPolicy.java Tue Jun 17 13:37:28 2008 +0400
@@ -556,8 +556,7 @@
* enabled, and focusable; <code>false</code> otherwise
*/
protected boolean accept(Component aComponent) {
- if (!(aComponent.isVisible() && aComponent.isDisplayable() &&
- aComponent.isFocusable() && aComponent.isEnabled())) {
+ if (!aComponent.canBeFocusOwner()) {
return false;
}
--- a/jdk/src/share/classes/java/awt/DefaultKeyboardFocusManager.java Wed Jun 11 01:31:42 2008 -0700
+++ b/jdk/src/share/classes/java/awt/DefaultKeyboardFocusManager.java Tue Jun 17 13:37:28 2008 +0400
@@ -154,7 +154,7 @@
private boolean doRestoreFocus(Component toFocus, Component vetoedComponent,
boolean clearOnFailure)
{
- if (toFocus != vetoedComponent && toFocus.isShowing() && toFocus.isFocusable() &&
+ if (toFocus != vetoedComponent && toFocus.isShowing() && toFocus.canBeFocusOwner() &&
toFocus.requestFocus(false, CausedFocusEvent.Cause.ROLLBACK))
{
return true;
@@ -500,8 +500,11 @@
}
}
- if (!(newFocusOwner.isFocusable() && newFocusOwner.isEnabled()
- && newFocusOwner.isShowing()))
+ if (!(newFocusOwner.isFocusable() && newFocusOwner.isShowing() &&
+ // Refuse focus on a disabled component if the focus event
+ // isn't of UNKNOWN reason (i.e. not a result of a direct request
+ // but traversal, activation or system generated).
+ (newFocusOwner.isEnabled() || cause.equals(CausedFocusEvent.Cause.UNKNOWN))))
{
// we should not accept focus on such component, so reject it.
dequeueKeyEvents(-1, newFocusOwner);
@@ -742,8 +745,7 @@
public boolean dispatchKeyEvent(KeyEvent e) {
Component focusOwner = (((AWTEvent)e).isPosted) ? getFocusOwner() : e.getComponent();
- if (focusOwner != null && focusOwner.isShowing() &&
- focusOwner.isFocusable() && focusOwner.isEnabled()) {
+ if (focusOwner != null && focusOwner.isShowing() && focusOwner.canBeFocusOwner()) {
if (!e.isConsumed()) {
Component comp = e.getComponent();
if (comp != null && comp.isEnabled()) {
--- a/jdk/src/share/classes/java/awt/Window.java Wed Jun 11 01:31:42 2008 -0700
+++ b/jdk/src/share/classes/java/awt/Window.java Tue Jun 17 13:37:28 2008 +0400
@@ -3145,9 +3145,7 @@
Component previousComp = temporaryLostComponent;
// Check that "component" is an acceptable focus owner and don't store it otherwise
// - or later we will have problems with opposite while handling WINDOW_GAINED_FOCUS
- if (component == null
- || (component.isDisplayable() && component.isVisible() && component.isEnabled() && component.isFocusable()))
- {
+ if (component == null || component.canBeFocusOwner()) {
temporaryLostComponent = component;
} else {
temporaryLostComponent = null;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/Focus/NoAutotransferToDisabledCompTest/NoAutotransferToDisabledCompTest.java Tue Jun 17 13:37:28 2008 +0400
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ @test
+ @bug 4685768
+ @summary Tests that auto-transfering focus doesn't stuck on a disabled component.
+ @author Anton Tarasov: area=awt.focus
+ @library ../../regtesthelpers
+ @build Util
+ @run main NoAutotransferToDisabledCompTest
+*/
+
+import java.awt.Robot;
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.applet.Applet;
+import test.java.awt.regtesthelpers.Util;
+
+public class NoAutotransferToDisabledCompTest extends Applet {
+ Robot robot;
+ JFrame frame = new JFrame("Frame");
+ JButton b0 = new JButton("b0");
+ JButton b1 = new JButton("b1");
+ JButton b2 = new JButton("b2");
+
+ public static void main(String[] args) {
+ NoAutotransferToDisabledCompTest app = new NoAutotransferToDisabledCompTest();
+ app.init();
+ app.start();
+ }
+
+ public void init() {
+ robot = Util.createRobot();
+ frame.add(b0);
+ frame.add(b1);
+ frame.add(b2);
+ frame.setLayout(new FlowLayout());
+ frame.pack();
+
+ b1.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ b1.setEnabled(false);
+ b2.setEnabled(false);
+ }
+ });
+ }
+
+ public void start() {
+ Util.showWindowWait(frame);
+
+ // Request focus on b1.
+ if (!Util.focusComponent(b1, 2000)) {
+ throw new TestErrorException("couldn't focus " + b1);
+ }
+
+ // Activate b1.
+ robot.keyPress(KeyEvent.VK_SPACE);
+ robot.delay(50);
+ robot.keyRelease(KeyEvent.VK_SPACE);
+ Util.waitForIdle(robot);
+
+ // Check that focus has been transfered to b0.
+ if (!b0.hasFocus()) {
+ throw new TestFailedException("focus wasn't auto-transfered properly!");
+ }
+ System.out.println("Test passed.");
+ }
+}
+
+/**
+ * Thrown when the behavior being verified is found wrong.
+ */
+class TestFailedException extends RuntimeException {
+ TestFailedException(String msg) {
+ super("Test failed: " + msg);
+ }
+}
+
+/**
+ * Thrown when an error not related to the behavior being verified is encountered.
+ */
+class TestErrorException extends RuntimeException {
+ TestErrorException(String msg) {
+ super("Unexpected error: " + msg);
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/Focus/RequestFocusToDisabledCompTest/RequestFocusToDisabledCompTest.java Tue Jun 17 13:37:28 2008 +0400
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ @test
+ @bug 4685768
+ @summary Tests that it's possible to manually request focus on a disabled component.
+ @author Anton Tarasov: area=awt.focus
+ @library ../../regtesthelpers
+ @build Util
+ @run main RequestFocusToDisabledCompTest
+*/
+
+import java.awt.Robot;
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.applet.Applet;
+import test.java.awt.regtesthelpers.Util;
+
+public class RequestFocusToDisabledCompTest extends Applet {
+ Robot robot;
+ JFrame frame = new JFrame("Frame");
+ JButton b0 = new JButton("b0");
+ JButton b1 = new JButton("b1");
+
+ public static void main(String[] args) {
+ RequestFocusToDisabledCompTest app = new RequestFocusToDisabledCompTest();
+ app.init();
+ app.start();
+ }
+
+ public void init() {
+ robot = Util.createRobot();
+ frame.add(b0);
+ frame.add(b1);
+ frame.setLayout(new FlowLayout());
+ frame.pack();
+
+ b1.setEnabled(false);
+ }
+
+ public void start() {
+ Util.showWindowWait(frame);
+
+ if (!b0.hasFocus()) {
+ // Request focus on b0.
+ if (!Util.focusComponent(b0, 2000)) {
+ throw new TestErrorException("couldn't focus " + b0);
+ }
+ }
+
+ // Try to request focus on b1.
+ if (!Util.focusComponent(b1, 2000)) {
+ throw new TestFailedException("focus wasn't requested on disabled " + b1);
+ }
+ System.out.println("Test passed.");
+ }
+}
+
+/**
+ * Thrown when the behavior being verified is found wrong.
+ */
+class TestFailedException extends RuntimeException {
+ TestFailedException(String msg) {
+ super("Test failed: " + msg);
+ }
+}
+
+/**
+ * Thrown when an error not related to the behavior being verified is encountered.
+ */
+class TestErrorException extends RuntimeException {
+ TestErrorException(String msg) {
+ super("Unexpected error: " + msg);
+ }
+}
--- a/jdk/test/java/awt/regtesthelpers/Util.java Wed Jun 11 01:31:42 2008 -0700
+++ b/jdk/test/java/awt/regtesthelpers/Util.java Tue Jun 17 13:37:28 2008 +0400
@@ -124,6 +124,14 @@
}
/**
+ * Makes the window visible and waits until it's shown.
+ */
+ public static void showWindowWait(Window win) {
+ win.setVisible(true);
+ waitTillShown(win);
+ }
+
+ /**
* Moves mouse pointer in the center of given {@code comp} component
* using {@code robot} parameter.
*/
@@ -574,4 +582,22 @@
public static boolean trackActionPerformed(Button button, Runnable action, int time, boolean printEvent) {
return trackEvent(ActionEvent.ACTION_PERFORMED, button, action, time, printEvent);
}
+
+ /*
+ * Requests focus on the component provided and waits for the result.
+ * @return true if the component has been focused, false otherwise.
+ */
+ public static boolean focusComponent(Component comp, int time) {
+ return focusComponent(comp, time, false);
+ }
+ public static boolean focusComponent(final Component comp, int time, boolean printEvent) {
+ return trackFocusGained(comp,
+ new Runnable() {
+ public void run() {
+ comp.requestFocus();
+ }
+ },
+ time, printEvent);
+
+ }
}