1 /* 2 * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /** 25 * @test 26 * @key headful 27 * @bug 8154043 8172509 28 * @summary Fields not reachable anymore by tab-key, because of new tabbing 29 * behaviour of radio button groups. 30 * @run main ButtonGroupLayoutTraversalTest 31 */ 32 33 import java.awt.BorderLayout; 34 import java.awt.Color; 35 import java.awt.GridLayout; 36 import java.awt.Robot; 37 import java.awt.event.FocusAdapter; 38 import java.awt.event.FocusEvent; 39 import java.awt.event.KeyEvent; 40 import javax.swing.JFrame; 41 import javax.swing.SwingUtilities; 42 import javax.swing.UIManager; 43 import javax.swing.JPanel; 44 import javax.swing.ButtonGroup; 45 import javax.swing.JComponent; 46 import javax.swing.JRadioButton; 47 import javax.swing.JToggleButton; 48 import javax.swing.LayoutFocusTraversalPolicy; 49 import javax.swing.UnsupportedLookAndFeelException; 50 51 public class ButtonGroupLayoutTraversalTest { 52 53 static int nx = 3; 54 static int ny = 3; 55 56 static int focusCnt[] = new int[nx * ny]; 57 private static JFrame window; 58 59 public static void main(String[] args) throws Exception { 60 61 SwingUtilities.invokeAndWait(() -> changeLAF()); 62 SwingUtilities.invokeAndWait(() -> initLayout(nx, ny)); 63 Robot robot = new Robot(); 64 robot.setAutoDelay(100); 65 robot.waitForIdle(); 66 robot.delay(200); 67 68 for (int i = 0; i < nx * ny - nx * ny / 2 - 1; i++) { 69 robot.keyPress(KeyEvent.VK_RIGHT); 70 robot.keyRelease(KeyEvent.VK_RIGHT); 71 } 72 73 for (int i = 0; i < nx * ny / 2; i++) { 74 robot.keyPress(KeyEvent.VK_TAB); 75 robot.keyRelease(KeyEvent.VK_TAB); 76 } 77 78 robot.waitForIdle(); 79 robot.delay(200); 80 81 for (int i = 0; i < nx * ny; i++) { 82 if (focusCnt[i] < 1) { 83 SwingUtilities.invokeLater(window::dispose); 84 throw new RuntimeException("Component " + i 85 + " is not reachable in the forward focus cycle"); 86 } else if (focusCnt[i] > 1) { 87 SwingUtilities.invokeLater(window::dispose); 88 throw new RuntimeException("Component " + i 89 + " got focus more than once in the forward focus cycle"); 90 } 91 } 92 93 for (int i = 0; i < nx * ny / 2; i++) { 94 robot.keyPress(KeyEvent.VK_SHIFT); 95 robot.keyPress(KeyEvent.VK_TAB); 96 robot.keyRelease(KeyEvent.VK_TAB); 97 robot.keyRelease(KeyEvent.VK_SHIFT); 98 } 99 100 for (int i = 0; i < nx * ny - nx * ny / 2 - 1; i++) { 101 robot.keyPress(KeyEvent.VK_LEFT); 102 robot.keyRelease(KeyEvent.VK_LEFT); 103 } 104 105 robot.keyPress(KeyEvent.VK_SHIFT); 106 robot.keyPress(KeyEvent.VK_TAB); 107 robot.keyRelease(KeyEvent.VK_TAB); 108 robot.keyRelease(KeyEvent.VK_SHIFT); 109 110 robot.waitForIdle(); 111 robot.delay(200); 112 113 for (int i = 0; i < nx * ny; i++) { 114 if (focusCnt[i] < 2) { 115 SwingUtilities.invokeLater(window::dispose); 116 throw new RuntimeException("Component " + i 117 + " is not reachable in the backward focus cycle"); 118 } else if (focusCnt[i] > 2) { 119 SwingUtilities.invokeLater(window::dispose); 120 throw new RuntimeException("Component " + i 121 + " got focus more than once in the backward focus cycle"); 122 } 123 } 124 125 SwingUtilities.invokeLater(window::dispose); 126 } 127 128 private static void changeLAF() { 129 String currentLAF = UIManager.getLookAndFeel().toString(); 130 currentLAF = currentLAF.toLowerCase(); 131 if (currentLAF.contains("aqua") || currentLAF.contains("nimbus")) { 132 try { 133 UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); 134 } catch (ClassNotFoundException 135 | IllegalAccessException 136 | InstantiationException 137 | UnsupportedLookAndFeelException ex) { 138 ex.printStackTrace(); 139 } 140 } 141 } 142 143 public static void initLayout(int nx, int ny) { 144 window = new JFrame("Test"); 145 window.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 146 JPanel rootPanel = new JPanel(); 147 rootPanel.setLayout(new BorderLayout()); 148 JPanel formPanel = new JPanel(new GridLayout(nx, ny)); 149 formPanel.setFocusTraversalPolicy(new LayoutFocusTraversalPolicy()); 150 formPanel.setFocusCycleRoot(true); 151 ButtonGroup radioButtonGroup = new ButtonGroup(); 152 for (int i = 0; i < nx * ny; i++) { 153 JToggleButton comp; 154 if (i % 2 == 0) { 155 comp = new JRadioButton("Grouped component"); 156 radioButtonGroup.add(comp); 157 } else { 158 comp = new JRadioButton("Single component"); 159 } 160 formPanel.add(comp); 161 int fi = i; 162 comp.setBackground(Color.red); 163 comp.addFocusListener(new FocusAdapter() { 164 @Override 165 public void focusGained(FocusEvent e) { 166 focusCnt[fi]++; 167 if (focusCnt[fi] == 1) { 168 ((JComponent) e.getSource()) 169 .setBackground(Color.yellow); 170 } else if (focusCnt[fi] == 2) { 171 ((JComponent) e.getSource()) 172 .setBackground(Color.green); 173 } else { 174 ((JComponent) e.getSource()) 175 .setBackground(Color.red); 176 } 177 } 178 }); 179 } 180 rootPanel.add(formPanel, BorderLayout.CENTER); 181 window.add(rootPanel); 182 window.pack(); 183 window.setVisible(true); 184 } 185 }