diff --git a/src/java.desktop/unix/classes/sun/awt/wl/WLComponentPeer.java b/src/java.desktop/unix/classes/sun/awt/wl/WLComponentPeer.java index 974d9bd5732b..b8d7683634c6 100644 --- a/src/java.desktop/unix/classes/sun/awt/wl/WLComponentPeer.java +++ b/src/java.desktop/unix/classes/sun/awt/wl/WLComponentPeer.java @@ -73,6 +73,7 @@ import java.awt.image.VolatileImage; import java.awt.peer.ComponentPeer; import java.awt.peer.ContainerPeer; +import java.awt.peer.KeyboardFocusManagerPeer; import java.util.ArrayList; import java.util.Objects; import java.util.function.Supplier; @@ -235,18 +236,37 @@ public boolean requestFocus(Component lightweightChild, boolean temporary, if (currentlyFocusedWindow == null) return false; - Window targetDecoratedWindow = getNativelyFocusableOwnerOrSelf(target); - if (currentlyFocusedWindow == targetDecoratedWindow) { - WLKeyboardFocusManagerPeer.deliverFocus(lightweightChild, - null, - true, - cause, - null); - } else { - return false; + if (WLKeyboardFocusManagerPeer. + processSynchronousLightweightTransfer(target, lightweightChild, temporary, + focusedWindowChangeAllowed, time)) { + return true; + } + + Window nativelyFocusableWindow = getNativelyFocusableOwnerOrSelf(target); + int result = WLKeyboardFocusManagerPeer. + shouldNativelyFocusHeavyweight(nativelyFocusableWindow, lightweightChild, + temporary, focusedWindowChangeAllowed, + time, cause, true); + + switch (result) { + case WLKeyboardFocusManagerPeer.SNFH_FAILURE: + return false; + + case WLKeyboardFocusManagerPeer.SNFH_SUCCESS_PROCEED: { + Component focusOwner = WLKeyboardFocusManagerPeer.getInstance().getCurrentFocusOwner(); + return WLKeyboardFocusManagerPeer.deliverFocus(lightweightChild, + target, + true, + cause, + focusOwner); + } + + case WLKeyboardFocusManagerPeer.SNFH_SUCCESS_HANDLED: + // Either lightweight or excessive request - all events are generated. + return true; } - return true; + return false; } private static Window getToplevelFor(Component component) { diff --git a/test/jdk/jb/java/awt/Focus/ContextMenuFocusTest.java b/test/jdk/jb/java/awt/Focus/ContextMenuFocusTest.java new file mode 100644 index 000000000000..16dcaa774f19 --- /dev/null +++ b/test/jdk/jb/java/awt/Focus/ContextMenuFocusTest.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, JetBrains s.r.o.. 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. + */ + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.SwingUtilities; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.concurrent.CountDownLatch; + +/** + * @test + * @summary Regression test for JBR-7072 Wayland: clicks on items of floating context menus are ignored + * @key headful + * + * @run main/manual ContextMenuFocusTest + */ + +public class ContextMenuFocusTest { + static volatile boolean passed = false; + static CountDownLatch latch = new CountDownLatch(1); + static JFrame frame; + public static void main(String[] args) throws InterruptedException { + runTest(); + latch.await(); + frame.dispose(); + if (!passed) throw new RuntimeException("Test failed"); + } + + private static void runTest() { + SwingUtilities.invokeLater(() -> { + frame = new JFrame("Focus Events Test"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + try { + JButton button = new JButton("Button"); + button.addFocusListener(new FocusListener() { + @Override + public void focusGained(FocusEvent e) { + button.setBackground(Color.GREEN); + } + + @Override + public void focusLost(FocusEvent e) { + if (!e.isTemporary()) { + button.setBackground(Color.RED); + } + } + }); + + JPopupMenu popup = new JPopupMenu("Edit"); + popup.add(new JMenuItem("Cut")); + popup.add(new JMenuItem("Copy")); + popup.add(new JMenuItem("Paste")); + + button.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent me) { + if (SwingUtilities.isRightMouseButton(me)) { + popup.show(me.getComponent(), me.getX(), me.getY()); + } + } + }); + + JButton passButton = new JButton("PASS"); + passButton.addActionListener((ActionEvent e) -> { + passed = true; + latch.countDown(); + }); + + JButton failButton = new JButton("FAIL"); + failButton.addActionListener((ActionEvent e) -> { + passed = false; + latch.countDown(); + }); + + + frame.add(new JLabel("

Instructions

" + + "

The button below stays green as long as it is in focus or " + + "has temporarily lost focus.

" + + ""), + BorderLayout.CENTER); + frame.add(button, BorderLayout.SOUTH); + frame.add(passButton, BorderLayout.EAST); + frame.add(failButton, BorderLayout.WEST); + frame.setSize(500, 800); + frame.setVisible(true); + + button.requestFocusInWindow(); + } catch (Exception e) { + frame.dispose(); + } + }); + } +} \ No newline at end of file