RFR: 8342096: Popup menus that request focus are not shown on Linux with Wayland [v3]
Alexander Zvegintsev
azvegint at openjdk.org
Tue Jan 21 11:29:35 UTC 2025
On Mon, 20 Jan 2025 19:56:18 GMT, Alexey Ivanov <aivanov at openjdk.org> wrote:
> What if the window that loses focus is not the parent of the popup hierarchy? Or is it guaranteed that all the children of the popup window receive the focus lost event?
I can't imagine such a case, but even if it is, it will all be handled correctly once we pass the input back to the XWayland server.
At the moment the popup menu is open, we have grabbed the input inside the Xserver, so any click outside the menu should result in ungrabbing and hiding all menus.
But XWayland is a special case, where we can't track mouse clicks outside of it in native apps and DE, so we have to implement this workaround by manually ungrabbing the input, so that our code can at least handle the lost window focus as a reason to dismiss the popup menu.
There are a few cases where this workaround does not work, e.g. when we click on or non-focusable native windows/panels (Dock, top panel in Gnome, etc.), or show a pop-up menu on our non-focusable window:
import java.awt.EventQueue;
import java.awt.Frame;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class AwtPopup {
public static void main(String[] args) throws Exception{
EventQueue.invokeAndWait(() -> {
final Frame f= new Frame("PopupMenu Example");
final PopupMenu popupmenu = new PopupMenu("Menu");
popupmenu.add(new MenuItem("Item"));
f.add(popupmenu);
f.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
popupmenu.show(f , e.getX(), e.getY());
}
});
f.setSize(200,200);
f.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
f.dispose();
}
});
f.setLocationRelativeTo(null);
// Menu is not hidden when we click on the native focusable window
// but it is hidden when we click on any X11 application
f.setFocusableWindowState(false);
f.setVisible(true);
});
}
}
It is not an ideal workaround, but it is good enough to exist.
> Shouldn't the line below
>
> https://github.com/openjdk/jdk/blob/b60a33269247c1039fa0ec6cd99476c9f8976852/src/java.desktop/unix/classes/sun/awt/UNIXToolkit.java#L563
> be changed to call dismissPopupOnFocusLostIfNeededCleanUp to remove the listener from the popup and its children?
Not really, because a few lines after we ungrab the input, so all the popup menu will be dismissed, performing cleanups in their `hide()` and `setVisible()` methods.
https://github.com/openjdk/jdk/blob/f54e0bf267280c270b0e181289498b28aaf36ee6/src/java.desktop/unix/classes/sun/awt/X11/XMenuWindow.java#L442-L444
https://github.com/openjdk/jdk/blob/f54e0bf267280c270b0e181289498b28aaf36ee6/src/java.desktop/share/classes/javax/swing/JPopupMenu.java#L824-L826
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/22729#discussion_r1923549477
More information about the client-libs-dev
mailing list