From 9bff8ffd7b86d1e278f5a878134fbe5175c1e0dd Mon Sep 17 00:00:00 2001 From: Takakuri Date: Thu, 1 Dec 2022 21:21:48 +0900 Subject: [PATCH] Backport eef0714323de --- jdk/src/share/classes/java/awt/Component.java | 18 +-- .../share/classes/javax/swing/JComponent.java | 16 +++ .../classes/javax/swing/JToggleButton.java | 37 ++++++ .../ButtonGroupFocusTest.java | 119 ++++++++++++++++++ 4 files changed, 181 insertions(+), 9 deletions(-) create mode 100644 jdk/test/javax/swing/JRadioButton/ButtonGroupFocus/ButtonGroupFocusTest.java diff --git a/jdk/src/share/classes/java/awt/Component.java b/jdk/src/share/classes/java/awt/Component.java index 314fb6f283e..862850c3ace 100644 --- a/jdk/src/share/classes/java/awt/Component.java +++ b/jdk/src/share/classes/java/awt/Component.java @@ -7759,6 +7759,15 @@ private boolean isRequestFocusAccepted(boolean temporary, } } + boolean ret = Component.requestFocusController.acceptRequestFocus(focusOwner, + this, + temporary, + focusedWindowChangeAllowed, + cause); + if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) { + focusLog.finest("RequestFocusController returns {0}", ret); + } + if (focusOwner == this || focusOwner == null) { // Controller is supposed to verify focus transfers and for this it // should know both from and to components. And it shouldn't verify @@ -7782,15 +7791,6 @@ private boolean isRequestFocusAccepted(boolean temporary, return true; } - boolean ret = Component.requestFocusController.acceptRequestFocus(focusOwner, - this, - temporary, - focusedWindowChangeAllowed, - cause); - if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) { - focusLog.finest("RequestFocusController returns {0}", ret); - } - return ret; } diff --git a/jdk/src/share/classes/javax/swing/JComponent.java b/jdk/src/share/classes/javax/swing/JComponent.java index cb64a4d0e78..b8861f162cf 100644 --- a/jdk/src/share/classes/javax/swing/JComponent.java +++ b/jdk/src/share/classes/javax/swing/JComponent.java @@ -3565,6 +3565,22 @@ public boolean acceptRequestFocus(Component from, Component to, return true; } + if (to instanceof JToggleButton) { + JToggleButton tb = ((JToggleButton)to).getGroupSelection(cause); + if (tb != to) { + if (focusedWindowChangeAllowed) { + SwingUtilities.invokeLater(() -> { + tb.requestFocus(); + }); + } else { + SwingUtilities.invokeLater(() -> { + tb.requestFocusInWindow(); + }); + } + return false; + } + } + if ((from == null) || !(from instanceof JComponent)) { return true; } diff --git a/jdk/src/share/classes/javax/swing/JToggleButton.java b/jdk/src/share/classes/javax/swing/JToggleButton.java index b48015dab1d..bab50eff666 100644 --- a/jdk/src/share/classes/javax/swing/JToggleButton.java +++ b/jdk/src/share/classes/javax/swing/JToggleButton.java @@ -35,6 +35,8 @@ import java.io.ObjectInputStream; import java.io.IOException; +import java.util.Enumeration; +import sun.awt.CausedFocusEvent; /** * An implementation of a two-state button. @@ -208,6 +210,41 @@ boolean shouldUpdateSelectedStateFromAction() { return true; } + JToggleButton getGroupSelection(CausedFocusEvent.Cause cause) { + switch (cause) { + case ACTIVATION: + case TRAVERSAL: + case TRAVERSAL_UP: + case TRAVERSAL_DOWN: + case TRAVERSAL_FORWARD: + case TRAVERSAL_BACKWARD: + ButtonModel model = getModel(); + JToggleButton selection = this; + if (model instanceof DefaultButtonModel) { + ButtonGroup group = ((DefaultButtonModel) model).getGroup(); + if (group != null && group.getSelection() != null + && !group.isSelected(model)) { + Enumeration iterator = + group.getElements(); + while (iterator.hasMoreElements()) { + AbstractButton member = iterator.nextElement(); + if (group.isSelected(member.getModel())) { + if (member instanceof JToggleButton && + member.isVisible() && member.isDisplayable() && + member.isEnabled() && member.isFocusable()) { + selection = (JToggleButton) member; + } + break; + } + } + } + } + return selection; + default: + return this; + } + } + // ********************************************************************* /** diff --git a/jdk/test/javax/swing/JRadioButton/ButtonGroupFocus/ButtonGroupFocusTest.java b/jdk/test/javax/swing/JRadioButton/ButtonGroupFocus/ButtonGroupFocusTest.java new file mode 100644 index 00000000000..f7118aa3fba --- /dev/null +++ b/jdk/test/javax/swing/JRadioButton/ButtonGroupFocus/ButtonGroupFocusTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8074883 + * @summary Tab key should move to focused button in a button group + * @run main ButtonGroupFocusTest + */ + +import javax.swing.*; +import java.awt.*; +import java.awt.event.KeyEvent; + +public class ButtonGroupFocusTest { + + private static JRadioButton button1; + private static JRadioButton button2; + private static JRadioButton button3; + private static JRadioButton button4; + private static JRadioButton button5; + private static Robot robot; + private static JFrame frame; + + public static void main(String[] args) throws Exception { + robot = new Robot(); + robot.setAutoDelay(100); + + SwingUtilities.invokeAndWait(() -> { + frame = new JFrame(); + Container contentPane = frame.getContentPane(); + contentPane.setLayout(new FlowLayout()); + button1 = new JRadioButton("Button 1"); + contentPane.add(button1); + button2 = new JRadioButton("Button 2"); + contentPane.add(button2); + button3 = new JRadioButton("Button 3"); + contentPane.add(button3); + button4 = new JRadioButton("Button 4"); + contentPane.add(button4); + button5 = new JRadioButton("Button 5"); + contentPane.add(button5); + ButtonGroup group = new ButtonGroup(); + group.add(button1); + group.add(button2); + group.add(button3); + + group = new ButtonGroup(); + group.add(button4); + group.add(button5); + + button2.setSelected(true); + + frame.pack(); + frame.setVisible(true); + }); + + robot.waitForIdle(); + robot.delay(200); + + SwingUtilities.invokeAndWait(() -> { + if( !button2.hasFocus() ) { + frame.dispose(); + throw new RuntimeException( + "Button 2 should get focus after activation"); + } + }); + + robot.keyPress(KeyEvent.VK_TAB); + robot.keyRelease(KeyEvent.VK_TAB); + + robot.waitForIdle(); + robot.delay(200); + + SwingUtilities.invokeAndWait(() -> { + if( !button4.hasFocus() ) { + frame.dispose(); + throw new RuntimeException( + "Button 4 should get focus"); + } + button3.setSelected(true); + }); + + robot.keyPress(KeyEvent.VK_TAB); + robot.keyRelease(KeyEvent.VK_TAB); + + robot.waitForIdle(); + robot.delay(200); + + SwingUtilities.invokeAndWait(() -> { + if( !button3.hasFocus() ) { + frame.dispose(); + throw new RuntimeException( + "selected Button 3 should get focus"); + } + }); + + SwingUtilities.invokeLater(frame::dispose); + } +}