11/*
2- * Copyright (c) 2016, 2017 , Oracle and/or its affiliates. All rights reserved.
2+ * Copyright (c) 2016, 2025 , Oracle and/or its affiliates. All rights reserved.
33 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44 *
55 * This code is free software; you can redistribute it and/or modify it
2828 * @run main ButtonGroupFocusTest
2929 */
3030
31- import javax .swing .*;
32- import java .awt .*;
31+ import java .awt .AWTEvent ;
32+ import java .awt .Container ;
33+ import java .awt .FlowLayout ;
34+ import java .awt .KeyboardFocusManager ;
35+ import java .awt .Rectangle ;
36+ import java .awt .Robot ;
37+ import java .awt .Toolkit ;
38+ import java .awt .event .FocusAdapter ;
39+ import java .awt .event .FocusEvent ;
3340import java .awt .event .KeyEvent ;
41+ import java .awt .image .BufferedImage ;
42+ import java .io .File ;
43+ import java .util .concurrent .CountDownLatch ;
3444
35- public class ButtonGroupFocusTest {
45+ import javax .imageio .ImageIO ;
46+ import javax .swing .ButtonGroup ;
47+ import javax .swing .JFrame ;
48+ import javax .swing .JRadioButton ;
49+ import javax .swing .SwingUtilities ;
50+
51+ import static java .awt .KeyboardFocusManager .getCurrentKeyboardFocusManager ;
52+ import static java .util .concurrent .TimeUnit .MILLISECONDS ;
53+ import static java .util .concurrent .TimeUnit .SECONDS ;
54+
55+ public final class ButtonGroupFocusTest {
3656
3757 private static JRadioButton button1 ;
3858 private static JRadioButton button2 ;
3959 private static JRadioButton button3 ;
4060 private static JRadioButton button4 ;
4161 private static JRadioButton button5 ;
42- private static Robot robot ;
62+
63+ private static final CountDownLatch button2FocusLatch = new CountDownLatch (1 );
64+ private static final CountDownLatch button3FocusLatch = new CountDownLatch (1 );
65+ private static final CountDownLatch button4FocusLatch = new CountDownLatch (1 );
66+
67+ private static final CountDownLatch button2FocusLatch2 = new CountDownLatch (2 );
68+
69+ private static final long FOCUS_TIMEOUT = 4 ;
70+
4371 private static JFrame frame ;
4472
4573 public static void main (String [] args ) throws Exception {
46- robot = new Robot ();
47- robot .setAutoDelay (100 );
74+ final Robot robot = new Robot ();
4875
4976 SwingUtilities .invokeAndWait (() -> {
50- frame = new JFrame ();
77+ frame = new JFrame ("ButtonGroupFocusTest" );
5178 Container contentPane = frame .getContentPane ();
5279 contentPane .setLayout (new FlowLayout ());
5380 button1 = new JRadioButton ("Button 1" );
@@ -60,6 +87,7 @@ public static void main(String[] args) throws Exception {
6087 contentPane .add (button4 );
6188 button5 = new JRadioButton ("Button 5" );
6289 contentPane .add (button5 );
90+
6391 ButtonGroup group = new ButtonGroup ();
6492 group .add (button1 );
6593 group .add (button2 );
@@ -69,52 +97,96 @@ public static void main(String[] args) throws Exception {
6997 group .add (button4 );
7098 group .add (button5 );
7199
100+ button2 .addFocusListener (new LatchFocusListener (button2FocusLatch ));
101+ button3 .addFocusListener (new LatchFocusListener (button3FocusLatch ));
102+ button4 .addFocusListener (new LatchFocusListener (button4FocusLatch ));
103+
104+ button2 .addFocusListener (new LatchFocusListener (button2FocusLatch2 ));
105+
72106 button2 .setSelected (true );
73107
108+ // Debugging aid: log focus owner changes...
109+ KeyboardFocusManager focusManager = getCurrentKeyboardFocusManager ();
110+ focusManager .addPropertyChangeListener ("focusOwner" ,
111+ e -> System .out .println (e .getPropertyName ()
112+ + "\n \t " + e .getOldValue ()
113+ + "\n \t " + e .getNewValue ()));
114+
115+ // ...and dispatched key events
116+ Toolkit .getDefaultToolkit ().addAWTEventListener (
117+ e -> System .out .println ("Dispatched " + e ),
118+ AWTEvent .KEY_EVENT_MASK );
119+
74120 frame .pack ();
121+ frame .setLocationRelativeTo (null );
75122 frame .setVisible (true );
76123 });
77124
78- robot .waitForIdle ();
79- robot .delay (200 );
80-
81- SwingUtilities .invokeAndWait (() -> {
82- if ( !button2 .hasFocus () ) {
83- frame .dispose ();
84- throw new RuntimeException (
85- "Button 2 should get focus after activation" );
125+ try {
126+ if (!button2FocusLatch .await (FOCUS_TIMEOUT , SECONDS )) {
127+ throw new RuntimeException ("Button 2 should get focus "
128+ + "after activation" );
86129 }
87- });
130+ robot .waitForIdle ();
131+ robot .delay (200 );
88132
89- robot .keyPress (KeyEvent .VK_TAB );
90- robot .keyRelease (KeyEvent .VK_TAB );
133+ System .out .println ("\n \n *** Tab 1st" );
134+ robot .keyPress (KeyEvent .VK_TAB );
135+ robot .keyRelease (KeyEvent .VK_TAB );
91136
92- robot .waitForIdle ();
93- robot .delay (200 );
137+ if (!button4FocusLatch .await (FOCUS_TIMEOUT , SECONDS )) {
138+ throw new RuntimeException ("Button 4 should get focus" );
139+ }
140+ robot .waitForIdle ();
141+ robot .delay (200 );
94142
95- SwingUtilities .invokeAndWait (() -> {
96- if ( !button4 .hasFocus () ) {
97- frame .dispose ();
98- throw new RuntimeException (
99- "Button 4 should get focus" );
143+ if (button2FocusLatch2 .await (1 , MILLISECONDS )) {
144+ throw new RuntimeException ("Focus moved back to Button 2" );
100145 }
101- button3 .setSelected (true );
102- });
103146
104- robot .keyPress (KeyEvent .VK_TAB );
105- robot .keyRelease (KeyEvent .VK_TAB );
147+ SwingUtilities .invokeAndWait (() -> button3 .setSelected (true ));
148+ robot .waitForIdle ();
149+ robot .delay (200 );
106150
107- robot .waitForIdle ();
108- robot .delay (200 );
151+ System .out .println ("\n \n *** Tab 2nd" );
152+ robot .keyPress (KeyEvent .VK_TAB );
153+ robot .keyRelease (KeyEvent .VK_TAB );
109154
110- SwingUtilities .invokeAndWait (() -> {
111- if ( !button3 .hasFocus () ) {
112- frame .dispose ();
113- throw new RuntimeException (
114- "selected Button 3 should get focus" );
155+ if (!button3FocusLatch .await (FOCUS_TIMEOUT , SECONDS )) {
156+ throw new RuntimeException ("Selected Button 3 should get focus" );
115157 }
116- });
158+ } catch (Exception e ) {
159+ BufferedImage image = robot .createScreenCapture (getFrameBounds ());
160+ ImageIO .write (image , "png" ,
161+ new File ("image.png" ));
162+
163+ SwingUtilities .invokeAndWait (() ->
164+ System .err .println ("Current focus owner: "
165+ + getCurrentKeyboardFocusManager ()
166+ .getFocusOwner ()));
167+
168+ throw e ;
169+ } finally {
170+ SwingUtilities .invokeAndWait (frame ::dispose );
171+ }
172+ }
173+
174+ private static Rectangle getFrameBounds () throws Exception {
175+ Rectangle [] bounds = new Rectangle [1 ];
176+ SwingUtilities .invokeAndWait (() -> bounds [0 ] = frame .getBounds ());
177+ return bounds [0 ];
178+ }
179+
180+ private static final class LatchFocusListener extends FocusAdapter {
181+ private final CountDownLatch focusGainedLatch ;
182+
183+ private LatchFocusListener (CountDownLatch focusGainedLatch ) {
184+ this .focusGainedLatch = focusGainedLatch ;
185+ }
117186
118- SwingUtilities .invokeLater (frame ::dispose );
187+ @ Override
188+ public void focusGained (FocusEvent e ) {
189+ focusGainedLatch .countDown ();
190+ }
119191 }
120192}
0 commit comments