From b1814d0a226ce40ed388da6794ddc366db143ca8 Mon Sep 17 00:00:00 2001 From: Maxim Kartashev Date: Mon, 4 Dec 2023 18:49:30 +0400 Subject: [PATCH] IDEA-339714 Wayland: some popup-like windows disappear immediately GitOrigin-RevId: bec8c2d404cd8615c73eb311335d3c3d1d5c5f91 --- .../src/com/intellij/ide/IdePopupManager.java | 80 ++++++++++++------- .../com/intellij/ui/WindowMouseListener.java | 11 ++- 2 files changed, 58 insertions(+), 33 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ide/IdePopupManager.java b/platform/platform-impl/src/com/intellij/ide/IdePopupManager.java index f0c890c6cce9..dad956dede7e 100644 --- a/platform/platform-impl/src/com/intellij/ide/IdePopupManager.java +++ b/platform/platform-impl/src/com/intellij/ide/IdePopupManager.java @@ -10,6 +10,7 @@ import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.wm.IdeFrame; import com.intellij.ui.jcef.JBCefBrowserBase; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.ui.StartupUiUtil; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; @@ -46,6 +47,20 @@ public final class IdePopupManager implements IdeEventQueue.EventDispatcher { LOG.warn("Skipped " + e); return false; } + + if (StartupUiUtil.isWaylandToolkit()) { + // Reasons for skipping 'focus lost'-like events on Wayland: + // - When a new popup window appears, the main frame looses focus, but the "opposite window" + // for that event is null (because Wayland); this can be solved by waiting a bit + // (several hundreds ms) for + // KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow() + // to become non-null. + // - When a (popup) window is dragged, it also looses focus (because Wayland); this one is + // not solvable because there's no guarantee that the focus will get back, nor is there + // a notification that the drag is actually happening. + return false; + } + if (!isPopupActive()) return false; Window sourceWindow = ((WindowEvent)e).getWindow(); @@ -57,36 +72,9 @@ public final class IdePopupManager implements IdeEventQueue.EventDispatcher { return false; } - Window focused = ((WindowEvent)e).getOppositeWindow(); - if (focused == null) { - focused = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow(); - if (focused == null) { - // Check if any browser is in focus (java focus can be in the process of transfer). - JBCefBrowserBase browser = JBCefBrowserBase.getFocusedBrowser(); - if (browser != null && browser.getComponent() != null) { - focused = SwingUtilities.getWindowAncestor(browser.getComponent()); - } - } - } - - Component ultimateParentForFocusedComponent = UIUtil.findUltimateParent(focused); - Component ultimateParentForEventWindow = UIUtil.findUltimateParent(sourceWindow); - - boolean shouldCloseAllPopup = false; - if (ultimateParentForEventWindow == null || ultimateParentForFocusedComponent == null) { - shouldCloseAllPopup = true; - } - - if (!shouldCloseAllPopup && ultimateParentForEventWindow instanceof IdeFrame ultimateParentWindowForEvent) { - if (ultimateParentWindowForEvent.isInFullScreen() - && !ultimateParentForFocusedComponent.equals(ultimateParentForEventWindow)) { - shouldCloseAllPopup = true; - } - } - - if (shouldCloseAllPopup) { - closeAllPopups(); - } + Window focusedWindow = ((WindowEvent)e).getOppositeWindow(); + maybeCloseAllPopups(focusedWindow, sourceWindow); + return false; } else if (e instanceof KeyEvent keyEvent) { // the following is copied from IdeKeyEventDispatcher @@ -116,6 +104,38 @@ public final class IdePopupManager implements IdeEventQueue.EventDispatcher { return false; } + private void maybeCloseAllPopups(Window focused, Window sourceWindow) { + if (focused == null) { + focused = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow(); + if (focused == null) { + // Check if any browser is in focus (java focus can be in the process of transfer). + JBCefBrowserBase browser = JBCefBrowserBase.getFocusedBrowser(); + if (browser != null && browser.getComponent() != null) { + focused = SwingUtilities.getWindowAncestor(browser.getComponent()); + } + } + } + + Component ultimateParentForFocusedComponent = UIUtil.findUltimateParent(focused); + Component ultimateParentForEventWindow = UIUtil.findUltimateParent(sourceWindow); + + boolean shouldCloseAllPopup = false; + if (ultimateParentForEventWindow == null || ultimateParentForFocusedComponent == null) { + shouldCloseAllPopup = true; + } + + if (!shouldCloseAllPopup && ultimateParentForEventWindow instanceof IdeFrame ultimateParentWindowForEvent) { + if (ultimateParentWindowForEvent.isInFullScreen() + && !ultimateParentForFocusedComponent.equals(ultimateParentForEventWindow)) { + shouldCloseAllPopup = true; + } + } + + if (shouldCloseAllPopup) { + closeAllPopups(); + } + } + public void push(IdePopupEventDispatcher dispatcher) { myDispatchStack.remove(dispatcher); myDispatchStack.add(dispatcher); diff --git a/platform/platform-impl/src/com/intellij/ui/WindowMouseListener.java b/platform/platform-impl/src/com/intellij/ui/WindowMouseListener.java index 57c21f9eddc3..f366ac63701b 100644 --- a/platform/platform-impl/src/com/intellij/ui/WindowMouseListener.java +++ b/platform/platform-impl/src/com/intellij/ui/WindowMouseListener.java @@ -2,6 +2,7 @@ package com.intellij.ui; +import com.intellij.util.ui.StartupUiUtil; import com.intellij.util.ui.UIUtil; import com.jetbrains.JBR; import org.intellij.lang.annotations.JdkConstants; @@ -210,8 +211,12 @@ abstract class WindowMouseListener extends MouseAdapter implements MouseInputLis protected void notifyResized() {} private static boolean jbrMoveSupported(Component component) { - // The JBR team states that isWindowMoveSupported works only for Frame/Dialog - return (component instanceof Frame || component instanceof Dialog) - && JBR.isWindowMoveSupported(); + if (StartupUiUtil.isWaylandToolkit()) { + return (component instanceof Window window) && window.getType() != Window.Type.POPUP; + } else { + // The JBR team states that isWindowMoveSupported works only for Frame/Dialog + return (component instanceof Frame || component instanceof Dialog) + && JBR.isWindowMoveSupported(); + } } }