提交 9841fcb8 编写于 作者: A ant

6607170: Focus not set by requestFocus

Summary: fixing/refactoring focus auto-transfer mechanism.
Reviewed-by: son
上级 9f55765d
......@@ -1327,12 +1327,15 @@ public abstract class Component implements ImageObserver, MenuContainer,
KeyboardFocusManager.clearMostRecentFocusOwner(this);
synchronized (getTreeLock()) {
enabled = false;
if (isFocusOwner()) {
// A disabled lw container is allowed to contain a focus owner.
if ((isFocusOwner() || (containsFocus() && !isLightweight())) &&
KeyboardFocusManager.isAutoFocusTransferEnabled())
{
// Don't clear the global focus owner. If transferFocus
// fails, we want the focus to stay on the disabled
// Component so that keyboard traversal, et. al. still
// makes sense to the user.
autoTransferFocus(false);
transferFocus(false);
}
ComponentPeer peer = this.peer;
if (peer != null) {
......@@ -1493,8 +1496,8 @@ public abstract class Component implements ImageObserver, MenuContainer,
synchronized (getTreeLock()) {
visible = false;
mixOnHiding(isLightweight());
if (containsFocus()) {
autoTransferFocus(true);
if (containsFocus() && KeyboardFocusManager.isAutoFocusTransferEnabled()) {
transferFocus(true);
}
ComponentPeer peer = this.peer;
if (peer != null) {
......@@ -6578,12 +6581,8 @@ public abstract class Component implements ImageObserver, MenuContainer,
}
synchronized (getTreeLock()) {
if (isFocusOwner()
&& KeyboardFocusManager.isAutoFocusTransferEnabled()
&& !nextFocusHelper())
{
KeyboardFocusManager.getCurrentKeyboardFocusManager().
clearGlobalFocusOwner();
if (isFocusOwner() && KeyboardFocusManager.isAutoFocusTransferEnabledFor(this)) {
transferFocus(true);
}
if (getContainer() != null && isAddNotifyComplete) {
......@@ -6718,8 +6717,8 @@ public abstract class Component implements ImageObserver, MenuContainer,
firePropertyChange("focusable", oldFocusable, focusable);
if (oldFocusable && !focusable) {
if (isFocusOwner()) {
autoTransferFocus(true);
if (isFocusOwner() && KeyboardFocusManager.isAutoFocusTransferEnabled()) {
transferFocus(true);
}
KeyboardFocusManager.clearMostRecentFocusOwner(this);
}
......@@ -7373,69 +7372,6 @@ public abstract class Component implements ImageObserver, MenuContainer,
}
}
private void autoTransferFocus(boolean clearOnFailure) {
Component toTest = KeyboardFocusManager.
getCurrentKeyboardFocusManager().getFocusOwner();
if (toTest != this) {
if (toTest != null) {
toTest.autoTransferFocus(clearOnFailure);
}
return;
}
// Check if there are pending focus requests. We shouldn't do
// auto-transfer if user has already took care of this
// component becoming ineligible to hold focus.
if (!KeyboardFocusManager.isAutoFocusTransferEnabled()) {
return;
}
// the following code will execute only if this Component is the focus
// owner
if (!(isDisplayable() && isVisible() && isEnabled() && isFocusable())) {
doAutoTransfer(clearOnFailure);
return;
}
toTest = getParent();
while (toTest != null && !(toTest instanceof Window)) {
if (!(toTest.isDisplayable() && toTest.isVisible() &&
(toTest.isEnabled() || toTest.isLightweight()))) {
doAutoTransfer(clearOnFailure);
return;
}
toTest = toTest.getParent();
}
}
private void doAutoTransfer(boolean clearOnFailure) {
if (focusLog.isLoggable(Level.FINER)) {
focusLog.log(Level.FINER, "this = " + this + ", clearOnFailure = " + clearOnFailure);
}
if (clearOnFailure) {
if (!nextFocusHelper()) {
if (focusLog.isLoggable(Level.FINER)) {
focusLog.log(Level.FINER, "clear global focus owner");
}
KeyboardFocusManager.getCurrentKeyboardFocusManager().
clearGlobalFocusOwner();
}
} else {
transferFocus();
}
}
/**
* Transfers the focus to the next component, as though this Component were
* the focus owner.
* @see #requestFocus()
* @since JDK1.1
*/
public void transferFocus() {
nextFocus();
}
/**
* Returns the Container which is the focus cycle root of this Component's
* focus traversal cycle. Each focus traversal cycle has only a single
......@@ -7475,31 +7411,51 @@ public abstract class Component implements ImageObserver, MenuContainer,
return (rootAncestor == container);
}
Container getTraversalRoot() {
return getFocusCycleRootAncestor();
}
/**
* Transfers the focus to the next component, as though this Component were
* the focus owner.
* @see #requestFocus()
* @since JDK1.1
*/
public void transferFocus() {
nextFocus();
}
/**
* @deprecated As of JDK version 1.1,
* replaced by transferFocus().
*/
@Deprecated
public void nextFocus() {
nextFocusHelper();
transferFocus(false);
}
private boolean nextFocusHelper() {
Component toFocus = preNextFocusHelper();
boolean transferFocus(boolean clearOnFailure) {
if (focusLog.isLoggable(Level.FINER)) {
focusLog.log(Level.FINER, "toFocus = " + toFocus);
focusLog.finer("clearOnFailure = " + clearOnFailure);
}
if (isFocusOwner() && toFocus == this) {
return false;
Component toFocus = getNextFocusCandidate();
boolean res = false;
if (toFocus != null && !toFocus.isFocusOwner() && toFocus != this) {
res = toFocus.requestFocusInWindow(CausedFocusEvent.Cause.TRAVERSAL_FORWARD);
}
return postNextFocusHelper(toFocus, CausedFocusEvent.Cause.TRAVERSAL_FORWARD);
}
Container getTraversalRoot() {
return getFocusCycleRootAncestor();
if (clearOnFailure && !res) {
if (focusLog.isLoggable(Level.FINER)) {
focusLog.finer("clear global focus owner");
}
KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner();
}
if (focusLog.isLoggable(Level.FINER)) {
focusLog.finer("returning result: " + res);
}
return res;
}
final Component preNextFocusHelper() {
final Component getNextFocusCandidate() {
Container rootAncestor = getTraversalRoot();
Component comp = this;
while (rootAncestor != null &&
......@@ -7511,18 +7467,19 @@ public abstract class Component implements ImageObserver, MenuContainer,
rootAncestor = comp.getFocusCycleRootAncestor();
}
if (focusLog.isLoggable(Level.FINER)) {
focusLog.log(Level.FINER, "comp = " + comp + ", root = " + rootAncestor);
focusLog.finer("comp = " + comp + ", root = " + rootAncestor);
}
Component candidate = null;
if (rootAncestor != null) {
FocusTraversalPolicy policy = rootAncestor.getFocusTraversalPolicy();
Component toFocus = policy.getComponentAfter(rootAncestor, comp);
if (focusLog.isLoggable(Level.FINER)) {
focusLog.log(Level.FINER, "component after is " + toFocus);
focusLog.finer("component after is " + toFocus);
}
if (toFocus == null) {
toFocus = policy.getDefaultComponent(rootAncestor);
if (focusLog.isLoggable(Level.FINER)) {
focusLog.log(Level.FINER, "default component is " + toFocus);
focusLog.finer("default component is " + toFocus);
}
}
if (toFocus == null) {
......@@ -7531,23 +7488,12 @@ public abstract class Component implements ImageObserver, MenuContainer,
toFocus = applet;
}
}
return toFocus;
candidate = toFocus;
}
return null;
}
static boolean postNextFocusHelper(Component toFocus, CausedFocusEvent.Cause cause) {
if (toFocus != null) {
if (focusLog.isLoggable(Level.FINER)) {
focusLog.log(Level.FINER, "Next component " + toFocus);
}
boolean res = toFocus.requestFocusInWindow(cause);
if (focusLog.isLoggable(Level.FINER)) {
focusLog.log(Level.FINER, "Request focus returned " + res);
}
return res;
if (focusLog.isLoggable(Level.FINER)) {
focusLog.finer("Focus transfer candidate: " + candidate);
}
return false;
return candidate;
}
/**
......@@ -7557,6 +7503,10 @@ public abstract class Component implements ImageObserver, MenuContainer,
* @since 1.4
*/
public void transferFocusBackward() {
transferFocusBackward(false);
}
boolean transferFocusBackward(boolean clearOnFailure) {
Container rootAncestor = getTraversalRoot();
Component comp = this;
while (rootAncestor != null &&
......@@ -7567,6 +7517,7 @@ public abstract class Component implements ImageObserver, MenuContainer,
comp = rootAncestor;
rootAncestor = comp.getFocusCycleRootAncestor();
}
boolean res = false;
if (rootAncestor != null) {
FocusTraversalPolicy policy = rootAncestor.getFocusTraversalPolicy();
Component toFocus = policy.getComponentBefore(rootAncestor, comp);
......@@ -7574,9 +7525,19 @@ public abstract class Component implements ImageObserver, MenuContainer,
toFocus = policy.getDefaultComponent(rootAncestor);
}
if (toFocus != null) {
toFocus.requestFocusInWindow(CausedFocusEvent.Cause.TRAVERSAL_BACKWARD);
res = toFocus.requestFocusInWindow(CausedFocusEvent.Cause.TRAVERSAL_BACKWARD);
}
}
if (!res) {
if (focusLog.isLoggable(Level.FINER)) {
focusLog.finer("clear global focus owner");
}
KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner();
}
if (focusLog.isLoggable(Level.FINER)) {
focusLog.finer("returning result: " + res);
}
return res;
}
/**
......@@ -7651,6 +7612,20 @@ public abstract class Component implements ImageObserver, MenuContainer,
return hasFocus();
}
/*
* Used to disallow auto-focus-transfer on disposal of the focus owner
* in the process of disposing its parent container.
*/
private boolean autoFocusTransferOnDisposal = true;
void setAutoFocusTransferOnDisposal(boolean value) {
autoFocusTransferOnDisposal = value;
}
boolean isAutoFocusTransferOnDisposal() {
return autoFocusTransferOnDisposal;
}
/**
* Adds the specified popup menu to the component.
* @param popup the popup menu to be added to the component.
......
......@@ -2660,9 +2660,26 @@ public class Container extends Component {
synchronized (getTreeLock()) {
int ncomponents = this.ncomponents;
Component component[] = this.component;
for (int i = ncomponents-1 ; i >= 0 ; i--) {
if( component[i] != null )
component[i].removeNotify();
for (int i = ncomponents - 1; i >= 0; i--) {
if( component[i] != null ) {
// Fix for 6607170.
// We want to suppress focus change on disposal
// of the focused component. But because of focus
// is asynchronous, we should suppress focus change
// on every component in case it receives native focus
// in the process of disposal.
component[i].setAutoFocusTransferOnDisposal(false);
component[i].removeNotify();
component[i].setAutoFocusTransferOnDisposal(true);
}
}
// If some of the children had focus before disposal then it still has.
// Auto-transfer focus to the next (or previous) component if auto-transfer
// is enabled.
if (containsFocus() && KeyboardFocusManager.isAutoFocusTransferEnabledFor(this)) {
if (!transferFocus(false)) {
transferFocusBackward(true);
}
}
if ( dispatcher != null ) {
dispatcher.dispose();
......
......@@ -155,12 +155,13 @@ public class DefaultKeyboardFocusManager extends KeyboardFocusManager {
boolean clearOnFailure)
{
if (toFocus != vetoedComponent && toFocus.isShowing() && toFocus.isFocusable() &&
toFocus.requestFocus(false, CausedFocusEvent.Cause.ROLLBACK)) {
toFocus.requestFocus(false, CausedFocusEvent.Cause.ROLLBACK))
{
return true;
} else {
Component nextFocus = toFocus.preNextFocusHelper();
if (nextFocus != vetoedComponent
&& Component.postNextFocusHelper(nextFocus, CausedFocusEvent.Cause.ROLLBACK))
Component nextFocus = toFocus.getNextFocusCandidate();
if (nextFocus != null && nextFocus != vetoedComponent &&
nextFocus.requestFocusInWindow(CausedFocusEvent.Cause.ROLLBACK))
{
return true;
} else if (clearOnFailure) {
......@@ -504,9 +505,16 @@ public class DefaultKeyboardFocusManager extends KeyboardFocusManager {
{
// we should not accept focus on such component, so reject it.
dequeueKeyEvents(-1, newFocusOwner);
if (KeyboardFocusManager.isAutoFocusTransferEnabled())
{
restoreFocus(fe, newFocusedWindow);
if (KeyboardFocusManager.isAutoFocusTransferEnabled()) {
// If FOCUS_GAINED is for a disposed component (however
// it shouldn't happen) its toplevel parent is null. In this
// case we have to try to restore focus in the current focused
// window (for the details: 6607170).
if (newFocusedWindow == null) {
restoreFocus(fe, currentFocusedWindow);
} else {
restoreFocus(fe, newFocusedWindow);
}
}
break;
}
......
......@@ -2578,6 +2578,10 @@ public abstract class KeyboardFocusManager
}
}
static boolean isAutoFocusTransferEnabledFor(Component comp) {
return isAutoFocusTransferEnabled() && comp.isAutoFocusTransferOnDisposal();
}
/*
* Used to process exceptions in dispatching focus event (in focusLost/focusGained callbacks).
* @param ex previously caught exception that may be processed right here, or null
......
......@@ -96,12 +96,12 @@ public class XKeyboardFocusManagerPeer implements KeyboardFocusManagerPeer {
Component focusOwner = activeWindow.getFocusOwner();
if (focusLog.isLoggable(Level.FINE)) focusLog.fine("Clearing global focus owner " + focusOwner);
if (focusOwner != null) {
XComponentPeer nativePeer = XComponentPeer.getNativeContainer(focusOwner);
if (nativePeer != null) {
// XComponentPeer nativePeer = XComponentPeer.getNativeContainer(focusOwner);
// if (nativePeer != null) {
FocusEvent fl = new CausedFocusEvent(focusOwner, FocusEvent.FOCUS_LOST, false, null,
CausedFocusEvent.Cause.CLEAR_GLOBAL_FOCUS_OWNER);
XWindow.sendEvent(fl);
}
// }
}
}
}
......
......@@ -903,8 +903,27 @@ void AwtComponent::Show()
void AwtComponent::Hide()
{
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
jobject peer = GetPeer(env);
BOOL oldValue = sm_suppressFocusAndActivation;
m_visible = false;
// On disposal the focus owner actually loses focus at the moment of hiding.
// So, focus change suppression (if requested) should be made here.
if (GetHWnd() == sm_focusOwner &&
!JNU_CallMethodByName(env, NULL, peer, "isAutoFocusTransferOnDisposal", "()Z").z)
{
sm_suppressFocusAndActivation = TRUE;
// The native system may autotransfer focus on hiding to the parent
// of the component. Nevertheless this focus change won't be posted
// to the Java level, we're better to avoid this. Anyway, after
// the disposal focus should be requested to the right component.
::SetFocus(NULL);
sm_focusOwner = NULL;
}
::ShowWindow(GetHWnd(), SW_HIDE);
sm_suppressFocusAndActivation = oldValue;
}
BOOL
......
/*
* 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 6607170
@summary Tests for focus-auto-transfer.
@author Anton Tarasov: area=awt-focus
@library ../../regtesthelpers
@build Util
@run main ContainerFocusAutoTransferTest
*/
import java.applet.Applet;
import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.ComponentOrientation;
import java.awt.DefaultKeyboardFocusManager;
import java.awt.KeyboardFocusManager;
import java.awt.Robot;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.FocusEvent;
import java.awt.event.WindowEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import test.java.awt.regtesthelpers.Util;
public class ContainerFocusAutoTransferTest extends Applet {
Robot robot;
TestFrame frame;
KeyboardFocusManager kfm;
enum TestCase {
REMOVAL { public String toString() { return "removal"; } },
HIDING { public String toString() { return "hiding"; } },
DISABLING { public String toString() { return "disabling"; } },
DEFOCUSING { public String toString() { return "defocusing"; } };
public abstract String toString();
};
public static void main(String[] args) {
ContainerFocusAutoTransferTest app = new ContainerFocusAutoTransferTest();
app.init();
app.start();
}
public void init() {
robot = Util.createRobot();
kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
public void eventDispatched(AWTEvent event) {
System.out.println("--> " + event);
}
}, FocusEvent.FOCUS_EVENT_MASK | WindowEvent.WINDOW_FOCUS_EVENT_MASK);
}
public void start() {
System.out.println("*** TEST #1 ***");
test(TestCase.HIDING);
System.out.println("*** TEST #2 ***");
test(TestCase.REMOVAL);
System.out.println("*** TEST #3 ***");
test3(TestCase.DISABLING);
System.out.println("*** TEST #4 ***");
test3(TestCase.DEFOCUSING);
System.out.println("*** TEST #5 ***");
test4();
System.out.println("Test passed.");
}
void test(final TestCase t) {
showFrame();
test1(t); // Test for correct auto-transfer
test2(t); // Test for clearing focus
}
void test1(final TestCase t) {
Runnable action = new Runnable() {
public void run() {
KeyboardFocusManager.setCurrentKeyboardFocusManager(new TestKFM());
if (t == TestCase.REMOVAL) {
frame.remove(frame.panel0);
} else if (t == TestCase.HIDING) {
frame.panel0.setVisible(false);
}
frame.repaint();
}
};
if (!Util.trackFocusGained(frame.b3, action, 2000, false)) {
throw new TestFailedException(t + ": focus wasn't transfered as expected!");
}
KeyboardFocusManager.setCurrentKeyboardFocusManager(kfm);
}
void test2(TestCase t) {
frame.setFocusable(false); // exclude it from the focus cycle
if (t == TestCase.REMOVAL) {
frame.remove(frame.panel1);
} else if (t == TestCase.HIDING) {
frame.panel1.setVisible(false);
}
frame.repaint();
Util.waitForIdle(robot);
if (kfm.getFocusOwner() != null) {
throw new TestFailedException(t + ": focus wasn't cleared!");
}
}
void test3(final TestCase t) {
showFrame();
Runnable action = new Runnable() {
public void run() {
if (t == TestCase.DISABLING) {
frame.b0.setEnabled(false);
} else if (t == TestCase.DEFOCUSING) {
frame.b0.setFocusable(false);
}
}};
if (!Util.trackFocusGained(frame.b1, action, 2000, false)) {
throw new TestFailedException(t + ": focus wasn't transfered as expected!");
}
}
void test4() {
showFrame();
frame.setFocusableWindowState(false);
Util.waitForIdle(robot);
if (kfm.getFocusOwner() != null) {
throw new TestFailedException("defocusing the frame: focus wasn't cleared!");
}
}
void showFrame() {
if (frame != null) {
frame.dispose();
Util.waitForIdle(robot);
}
frame = new TestFrame();
frame.setVisible(true);
Util.waitTillShown(frame);
if (!frame.b0.hasFocus()) {
Util.clickOnComp(frame.b0, robot);
Util.waitForIdle(robot);
if (!frame.b0.hasFocus()) {
throw new TestErrorException("couldn't set focus on " + frame.b2);
}
}
}
class TestKFM extends DefaultKeyboardFocusManager {
public boolean dispatchEvent(AWTEvent e) {
if (e.getID() == FocusEvent.FOCUS_GAINED) {
System.out.println(e);
Component src = (Component)e.getSource();
if (src == frame.b1 || src == frame.b2) {
throw new TestFailedException("wrong focus transfer on removal!");
}
}
return super.dispatchEvent(e);
}
}
}
class TestFrame extends JFrame {
public JPanel panel0 = new JPanel();
public JPanel panel1 = new JPanel();
public JButton b0 = new JButton("b0");
public JButton b1 = new JButton("b1");
public JButton b2 = new JButton("b2");
public JButton b3 = new JButton("b3");
public JButton b4 = new JButton("b4");
public TestFrame() {
super("TestFrame");
// The change of the orientation and the reverse order of
// adding the buttons to the panel is because in Container.removeNotify()
// the child components are removed in the reverse order.
// We want that the focus owner (b0) would be removed first and
// that the next traversable component would be b1.
panel0.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
panel0.add(b2);
panel0.add(b1);
panel0.add(b0);
panel1.add(b3);
panel1.add(b4);
setLayout(new FlowLayout());
add(panel0);
add(panel1);
pack();
panel0.setBackground(Color.red);
panel1.setBackground(Color.blue);
}
}
// 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);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册