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.