Skip to content

Commit fcf9ebd

Browse files
Sonia Zaldana Callesshipilev
Sonia Zaldana Calles
authored andcommittedJan 17, 2024
8319103: Popups that request focus are not shown on Linux with Wayland
Backport-of: f3ed27582e16c3a323f590863cbeec6d35e20b58
1 parent 9ca8761 commit fcf9ebd

File tree

2 files changed

+123
-7
lines changed

2 files changed

+123
-7
lines changed
 

‎src/java.desktop/unix/classes/sun/awt/UNIXToolkit.java

+29-7
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,21 @@ public boolean isRunningOnWayland() {
502502
@Override
503503
public void windowLostFocus(WindowEvent e) {
504504
Window window = e.getWindow();
505+
Window oppositeWindow = e.getOppositeWindow();
506+
507+
// The focus can move between the window calling the popup,
508+
// and the popup window itself.
509+
// We only dismiss the popup in other cases.
510+
if (oppositeWindow != null) {
511+
if (window == oppositeWindow.getParent() ) {
512+
addWaylandWindowFocusListenerToWindow(oppositeWindow);
513+
return;
514+
}
515+
if (window.getParent() == oppositeWindow) {
516+
return;
517+
}
518+
}
519+
505520
window.removeWindowFocusListener(this);
506521

507522
// AWT
@@ -516,18 +531,22 @@ public void windowLostFocus(WindowEvent e) {
516531
}
517532
}
518533

534+
private static void addWaylandWindowFocusListenerToWindow(Window window) {
535+
if (!Arrays
536+
.asList(window.getWindowFocusListeners())
537+
.contains(waylandWindowFocusListener)
538+
) {
539+
window.addWindowFocusListener(waylandWindowFocusListener);
540+
}
541+
}
542+
519543
@Override
520544
public void dismissPopupOnFocusLostIfNeeded(Window invoker) {
521-
if (!isOnWayland()
522-
|| invoker == null
523-
|| Arrays
524-
.asList(invoker.getWindowFocusListeners())
525-
.contains(waylandWindowFocusListener)
526-
) {
545+
if (!isOnWayland() || invoker == null) {
527546
return;
528547
}
529548

530-
invoker.addWindowFocusListener(waylandWindowFocusListener);
549+
addWaylandWindowFocusListenerToWindow(invoker);
531550
}
532551

533552
@Override
@@ -537,5 +556,8 @@ public void dismissPopupOnFocusLostIfNeededCleanUp(Window invoker) {
537556
}
538557

539558
invoker.removeWindowFocusListener(waylandWindowFocusListener);
559+
for (Window ownedWindow : invoker.getOwnedWindows()) {
560+
ownedWindow.removeWindowFocusListener(waylandWindowFocusListener);
561+
}
540562
}
541563
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Copyright (c) 2023, 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 8319103
28+
* @requires (os.family == "linux")
29+
* @library /java/awt/regtesthelpers
30+
* @build PassFailJFrame
31+
* @summary Tests if the focusable popup can be dismissed when the parent
32+
* window or the popup itself loses focus in Wayland.
33+
* @run main/manual FocusablePopupDismissTest
34+
*/
35+
36+
import javax.swing.JButton;
37+
import javax.swing.JFrame;
38+
import javax.swing.JPopupMenu;
39+
import javax.swing.JTextField;
40+
import java.awt.Window;
41+
import java.util.List;
42+
43+
public class FocusablePopupDismissTest {
44+
private static final String INSTRUCTIONS = """
45+
A frame with a "Click me" button should appear next to the window
46+
with this instruction.
47+
48+
Click on the "Click me" button.
49+
50+
If the JTextField popup with "Some text" is not showing on the screen,
51+
click Fail.
52+
53+
The following steps require some focusable system window to be displayed
54+
on the screen. This could be a system settings window, file manager, etc.
55+
56+
Click on the "Click me" button if the popup is not displayed
57+
on the screen.
58+
59+
While the popup is displayed, click on some other window on the desktop.
60+
If the popup has disappeared, click Pass, otherwise click Fail.
61+
""";
62+
63+
public static void main(String[] args) throws Exception {
64+
if (System.getenv("WAYLAND_DISPLAY") == null) {
65+
//test is valid only when running on Wayland.
66+
return;
67+
}
68+
69+
PassFailJFrame.builder()
70+
.title("FocusablePopupDismissTest")
71+
.instructions(INSTRUCTIONS)
72+
.rows(20)
73+
.columns(45)
74+
.testUI(FocusablePopupDismissTest::createTestUI)
75+
.build()
76+
.awaitAndCheck();
77+
}
78+
79+
static List<Window> createTestUI() {
80+
JFrame frame = new JFrame("FocusablePopupDismissTest");
81+
JButton button = new JButton("Click me");
82+
frame.add(button);
83+
84+
button.addActionListener(e -> {
85+
JPopupMenu popupMenu = new JPopupMenu();
86+
JTextField textField = new JTextField("Some text", 10);
87+
popupMenu.add(textField);
88+
popupMenu.show(button, 0, button.getHeight());
89+
});
90+
frame.pack();
91+
92+
return List.of(frame);
93+
}
94+
}

0 commit comments

Comments
 (0)