On Mon, 20 Jan 2025 19:56:18 GMT, Alexey Ivanov <aiva...@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

Reply via email to