From 43c46ce1ec756b9e57a30cd3bbb8f9dbb842aea4 Mon Sep 17 00:00:00 2001 From: Sergei Tachenov Date: Fri, 27 Feb 2026 12:59:20 +0200 Subject: [PATCH] [UI, Wayland] IJPL-235513 Close the Show Usages popup when a usage is selected A rather hacky way, but it should do the trick. An alternative approach: somehow track the navigation and close the popup manually when it's done. But it's riskier and would potentially affect non-Wayland environments. (cherry picked from commit 65990e6a178e81b663f3d411dd8ad7b7b2bd8063) IJ-CR-193931 GitOrigin-RevId: e2ded5b02d7cae6d88968b55ffe5748a090bf858 --- .../find/actions/ShowUsagesTable.java | 5 +++ .../com/intellij/ui/popup/AbstractPopup.java | 36 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesTable.java b/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesTable.java index c65ad5516a45..3b7e9ab8b1b5 100644 --- a/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesTable.java +++ b/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesTable.java @@ -20,6 +20,7 @@ import com.intellij.ui.ScrollingUtil; import com.intellij.ui.SpeedSearchBase; import com.intellij.ui.SpeedSearchComparator; import com.intellij.ui.TableUtil; +import com.intellij.ui.popup.AbstractPopup; import com.intellij.ui.popup.HintUpdateSupply; import com.intellij.ui.table.JBTable; import com.intellij.usageView.UsageInfo; @@ -215,6 +216,10 @@ public final class ShowUsagesTable extends JBTable implements UiDataProvider { navigateBlocking(parameters.project, navigatable, NavigationOptions.requestFocus(), dataContext); } } + var popup = PopupUtil.getPopupContainerFor(this); + if (popup instanceof AbstractPopup abstractPopup) { + abstractPopup.setForceCancelOnFocusLoss(true); // Disable the Wayland focus workaround and allow it to close. + } UsageNavigation.getInstance(parameters.project).navigate(usageInfosToNavigate, true, dataContext); } }; diff --git a/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java b/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java index f1863da9d1ab..b5ff20877add 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/AbstractPopup.java @@ -233,6 +233,7 @@ public class AbstractPopup implements JBPopup, ScreenAreaConsumer, AlignedPopup, private JComponent myPreferredFocusedComponent; private boolean myRequestFocus; private boolean myFocusable; + private boolean myForceCancelOnFocusLoss; private boolean myForcedHeavyweight; private boolean myLocateWithinScreen; private boolean myResizable; @@ -2321,6 +2322,39 @@ public class AbstractPopup implements JBPopup, ScreenAreaConsumer, AlignedPopup, myCancelOnClickOutside = cancelOnClickOutside; } + /** + * Disables Wayland-specific workarounds for popup focus loss. + *

+ * Normally a focusable popup is closed when another window gets focus. + * A non-focusable popup is also closed when that window is not an ancestor of the popup. + *

+ *

+ * However, on Wayland due to various focus quirks, this behavior is disabled by default, + * because the owner window may get focus for no reason. + *

+ *

+ * In most cases it isn't an issue. For example, when the user clicks outside the popup, + * then it's closed anyway because it's the click itself that closes the popup, not the resulting focus loss. + * In some cases, however, it might be necessary to react to focus changes. + * One such example is the show usages popup that opens a focused editor when a usage is selected. + * In this case, we rely on the focus change to close the popup. + *

+ *

+ * This function should be called whenever we do something and expect a focus change as a result, + * and that focus change should close the popup. + * This is, of course, a hack, and therefore is generally not recommended, + * provided there's a reliable way to just close the popup by explicitly calling {@link #cancel()} instead. + * But it's not always an option, as the activity triggered by the popup can be asynchronous in nature and hard to track, + * as it's the case with usage navigation. + *

+ * + * @param forceCancelOnFocusLoss pass {@code true} here to enable the normal behavior on Wayland + */ + @ApiStatus.Internal + public void setForceCancelOnFocusLoss(boolean forceCancelOnFocusLoss) { + myForceCancelOnFocusLoss = forceCancelOnFocusLoss; + } + @ApiStatus.Internal public void setIsMovable(boolean movable) { myMovable = movable; @@ -3036,6 +3070,8 @@ public class AbstractPopup implements JBPopup, ScreenAreaConsumer, AlignedPopup, return false; } + if (myForceCancelOnFocusLoss) return true; + // On Wayland focus gets temporarily transferred to popup's owner while the popup is being // interactively moved. // This is not a reason for cancelling the popup.