Skip to content

8154043: Fields not reachable anymore by tab-key, because of new tabbing behaviour of radio button groups. #285

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions jdk/src/share/classes/javax/swing/LayoutFocusTraversalPolicy.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 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
Expand All @@ -26,9 +26,9 @@

import java.awt.Component;
import java.awt.Container;
import java.awt.ComponentOrientation;
import java.util.Comparator;
import java.io.*;
import java.util.Enumeration;
import sun.awt.SunToolkit;


Expand Down Expand Up @@ -235,6 +235,30 @@ protected boolean accept(Component aComponent) {
JComboBox box = (JComboBox)aComponent;
return box.getUI().isFocusTraversable(box);
} else if (aComponent instanceof JComponent) {
if (SunToolkit.isInstanceOf(aComponent,
"javax.swing.JToggleButton")) {
ButtonModel buttonModel = ((JToggleButton) aComponent).getModel();
if (buttonModel != null && buttonModel instanceof DefaultButtonModel) {
DefaultButtonModel model = (DefaultButtonModel) buttonModel;
ButtonGroup group = model.getGroup();
if (group != null) {
Enumeration<AbstractButton> elements =
group.getElements();
int idx = 0;
while (elements.hasMoreElements()) {
AbstractButton member = elements.nextElement();
if (member.isVisible() && member.isDisplayable() &&
member.isEnabled() && member.isFocusable()) {
if (member == aComponent) {
return idx == 0;
}
idx++;
}
}
}
}
}

JComponent jComponent = (JComponent)aComponent;
InputMap inputMap = jComponent.getInputMap(JComponent.WHEN_FOCUSED,
false);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 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
Expand Down Expand Up @@ -437,21 +437,7 @@ boolean containsInGroup(Object obj){
// Check if the next object to gain focus belongs
// to the button group or not
Component getFocusTransferBaseComponent(boolean next){
Component focusBaseComp = activeBtn;
Container container = focusBaseComp.getFocusCycleRootAncestor();
if (container != null) {
FocusTraversalPolicy policy = container.getFocusTraversalPolicy();
Component comp = next ? policy.getComponentAfter(container, activeBtn)
: policy.getComponentBefore(container, activeBtn);

// If next component in the button group, use last/first button as base focus
// otherwise, use the activeBtn as the base focus
if (containsInGroup(comp)) {
focusBaseComp = next ? lastBtn : firstBtn;
}
}

return focusBaseComp;
return firstBtn;
}

boolean getButtonGroupInfo() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* 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 8154043
@summary Fields not reachable anymore by tab-key, because of new tabbing
behaviour of radio button groups.
@run main ButtonGroupLayoutTraversalTest
*/

import javax.swing.*;
import java.awt.*;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;

public class ButtonGroupLayoutTraversalTest {
static int nx = 3;
static int ny = 3;

static int focusCnt[] = new int[nx * ny];
private static JFrame window;


public static void main(String[] args) throws Exception {

SwingUtilities.invokeAndWait(()->initLayout(nx, ny));
Robot robot = new Robot();
robot.setAutoDelay(100);
robot.waitForIdle();
robot.delay(200);


for(int i = 0; i < nx * ny - nx * ny / 2 - 1; i++) {
robot.keyPress(KeyEvent.VK_RIGHT);
robot.keyRelease(KeyEvent.VK_RIGHT);
}

for(int i = 0; i < nx * ny / 2; i++) {
robot.keyPress(KeyEvent.VK_TAB);
robot.keyRelease(KeyEvent.VK_TAB);
}

robot.waitForIdle();
robot.delay(200);

for(int i = 0; i < nx * ny; i++) {
if(focusCnt[i] < 1) {
SwingUtilities.invokeLater(window::dispose);
throw new RuntimeException("Component " + i +
" is not reachable in the forward focus cycle");
} else if (focusCnt[i] > 1) {
SwingUtilities.invokeLater(window::dispose);
throw new RuntimeException("Component " + i +
" got focus more than once in the forward focus cycle");
}
}

for(int i = 0; i < nx * ny / 2; i++) {
robot.keyPress(KeyEvent.VK_SHIFT);
robot.keyPress(KeyEvent.VK_TAB);
robot.keyRelease(KeyEvent.VK_TAB);
robot.keyRelease(KeyEvent.VK_SHIFT);
}

for(int i = 0; i < nx * ny - nx * ny / 2 - 1; i++) {
robot.keyPress(KeyEvent.VK_LEFT);
robot.keyRelease(KeyEvent.VK_LEFT);
}

robot.keyPress(KeyEvent.VK_SHIFT);
robot.keyPress(KeyEvent.VK_TAB);
robot.keyRelease(KeyEvent.VK_TAB);
robot.keyRelease(KeyEvent.VK_SHIFT);

robot.waitForIdle();
robot.delay(200);

for(int i = 0; i < nx * ny; i++) {
if(focusCnt[i] < 2) {
SwingUtilities.invokeLater(window::dispose);
throw new RuntimeException("Component " + i +
" is not reachable in the backward focus cycle");
} else if (focusCnt[i] > 2) {
SwingUtilities.invokeLater(window::dispose);
throw new RuntimeException("Component " + i +
" got focus more than once in the backward focus cycle");
}
}

SwingUtilities.invokeLater(window::dispose);
}

public static void initLayout(int nx, int ny)
{
window = new JFrame("Test");
window.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JPanel rootPanel = new JPanel();
rootPanel.setLayout(new BorderLayout());
JPanel formPanel = new JPanel(new GridLayout(nx, ny));
formPanel.setFocusTraversalPolicy(new LayoutFocusTraversalPolicy());
formPanel.setFocusCycleRoot(true);
ButtonGroup radioButtonGroup = new ButtonGroup();
for(int i = 0; i < nx * ny; i++) {
JToggleButton comp;
if(i % 2 == 0) {
comp = new JRadioButton("Grouped component");
radioButtonGroup.add(comp);
} else {
comp = new JRadioButton("Single component");
}
formPanel.add(comp);
int fi = i;
comp.setBackground(Color.red);
comp.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
focusCnt[fi]++;
if( focusCnt[fi] == 1) {
((JComponent) e.getSource())
.setBackground(Color.yellow);
} else if(focusCnt[fi] == 2) {
((JComponent) e.getSource())
.setBackground(Color.green);
} else {
((JComponent) e.getSource())
.setBackground(Color.red);
}
}
});
}
rootPanel.add(formPanel, BorderLayout.CENTER);
window.add(rootPanel);
window.pack();
window.setVisible(true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright (c) 2017, 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 8182577
* @summary Verifies if moving focus via custom ButtonModel causes crash
* @run main DefaultButtonModelCrashTest
*/
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Point;
import java.awt.Robot;
import java.awt.event.KeyEvent;
import javax.swing.ButtonModel;
import javax.swing.DefaultButtonModel;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

public class DefaultButtonModelCrashTest {
private JFrame frame = null;
private JPanel panel;
private volatile Point p = null;

public static void main(String[] args) throws Exception {
new DefaultButtonModelCrashTest();
}

public DefaultButtonModelCrashTest() throws Exception {
try {
Robot robot = new Robot();
robot.setAutoDelay(200);
SwingUtilities.invokeAndWait(() -> go());
robot.waitForIdle();
robot.keyPress(KeyEvent.VK_TAB);
robot.keyRelease(KeyEvent.VK_TAB);
robot.delay(100);
robot.keyPress(KeyEvent.VK_TAB);
robot.keyRelease(KeyEvent.VK_TAB);
} finally {
SwingUtilities.invokeAndWait(()->frame .dispose());
}
}

private void go() {

frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container contentPane = frame.getContentPane();
ButtonModel model = new DefaultButtonModel();

JCheckBox check = new JCheckBox("a bit broken");
check.setModel(model);
panel = new JPanel(new BorderLayout());
panel.add(new JTextField("Press Tab (twice?)"), BorderLayout.NORTH);
panel.add(check);
contentPane.add(panel);
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
}
}
Loading