4685768: A11y issue - Focus set to disabled component, can't Tab/Shift-Tab
The restore-focus procedure should skip disabled components. Reviewed-by: art, dcherepanov
This commit is contained in:
parent
6a55242693
commit
7d7546ef37
@ -7488,9 +7488,7 @@ public abstract class Component implements ImageObserver, MenuContainer,
|
||||
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 @@ public abstract class Component implements ImageObserver, MenuContainer,
|
||||
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 @@ public abstract class Component implements ImageObserver, MenuContainer,
|
||||
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 @@ public abstract class Component implements ImageObserver, MenuContainer,
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
@ -860,11 +860,11 @@ public class Container extends Component {
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
@ -556,8 +556,7 @@ public class ContainerOrderFocusTraversalPolicy extends FocusTraversalPolicy
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
@ -154,7 +154,7 @@ public class DefaultKeyboardFocusManager extends KeyboardFocusManager {
|
||||
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 @@ public class DefaultKeyboardFocusManager extends KeyboardFocusManager {
|
||||
}
|
||||
}
|
||||
|
||||
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 class DefaultKeyboardFocusManager extends KeyboardFocusManager {
|
||||
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()) {
|
||||
|
@ -3145,9 +3145,7 @@ public class Window extends Container implements Accessible {
|
||||
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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -123,6 +123,14 @@ public final class Util {
|
||||
throw new RuntimeException("Unexpected toolkit - " + tk);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 final class Util {
|
||||
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);
|
||||
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user