From 766a1396f4c5537f73578c8de4c9fb65bfe402d9 Mon Sep 17 00:00:00 2001 From: Heiko Klare Date: Sun, 12 Jul 2026 21:31:56 +0200 Subject: [PATCH] Find/Replace overlay: separate command infrastructure into dedicated class Introduce FindReplaceOverlayCommandSupport to own the overlay's command-related infrastructure. As a first step, move the workaround that disables the target text editor's global action handlers (setTextEditorActionsActivated and DeactivateGlobalActionHandlers) out of FindReplaceOverlay into this new class. FindReplaceOverlay now only calls commandSupport.onFocusGained/Lost(). Contributes to https://github.com/eclipse-platform/eclipse.platform.ui/issues/1912 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../overlay/FindReplaceOverlay.java | 70 +----------- .../FindReplaceOverlayCommandSupport.java | 101 ++++++++++++++++++ 2 files changed, 106 insertions(+), 65 deletions(-) create mode 100644 bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/overlay/FindReplaceOverlayCommandSupport.java diff --git a/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/overlay/FindReplaceOverlay.java b/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/overlay/FindReplaceOverlay.java index cfe2c4739f6..ae88160cc86 100644 --- a/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/overlay/FindReplaceOverlay.java +++ b/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/overlay/FindReplaceOverlay.java @@ -13,11 +13,8 @@ *******************************************************************************/ package org.eclipse.ui.internal.findandreplace.overlay; -import java.lang.reflect.Method; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import org.osgi.framework.FrameworkUtil; @@ -47,9 +44,6 @@ import org.eclipse.swt.widgets.ToolItem; import org.eclipse.swt.widgets.Widget; -import org.eclipse.core.runtime.ILog; - -import org.eclipse.jface.action.IAction; import org.eclipse.jface.bindings.keys.KeyStroke; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.IDialogSettings; @@ -64,7 +58,6 @@ import org.eclipse.jface.text.IFindReplaceTarget; import org.eclipse.jface.text.ITextViewer; -import org.eclipse.ui.IActionBars; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.fieldassist.ContentAssistCommandAdapter; @@ -74,12 +67,9 @@ import org.eclipse.ui.internal.findandreplace.HistoryStore; import org.eclipse.ui.internal.findandreplace.IFindReplaceLogic; import org.eclipse.ui.internal.findandreplace.SearchOptions; -import org.eclipse.ui.part.MultiPageEditorSite; -import org.eclipse.ui.texteditor.AbstractTextEditor; import org.eclipse.ui.texteditor.FindReplaceAction; import org.eclipse.ui.texteditor.IAbstractTextEditorHelpContextIds; -import org.eclipse.ui.texteditor.ITextEditorActionConstants; import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; import org.eclipse.ui.texteditor.StatusTextEditor; @@ -159,69 +149,18 @@ private static final class KeyboardShortcuts { private ControlDecoration searchBarDecoration; private ContentAssistCommandAdapter contentAssistSearchField, contentAssistReplaceField; - private final FocusListener targetActionActivationHandling = new FocusListener() { - private DeactivateGlobalActionHandlers globalActionHandlerDeaction; + private FindReplaceOverlayCommandSupport commandSupport; + private final FocusListener targetActionActivationHandling = new FocusListener() { @Override public void focusGained(FocusEvent e) { - setTextEditorActionsActivated(false); + commandSupport.overlayActivated(); } @Override public void focusLost(FocusEvent e) { - setTextEditorActionsActivated(true); - } - - /* - * Adapted from - * org.eclipse.jdt.internal.ui.javaeditor.JavaEditor#setActionsActivated( - * boolean) - */ - private void setTextEditorActionsActivated(boolean state) { - if (!(targetPart instanceof AbstractTextEditor) || targetPart.getSite().getWorkbenchWindow().isClosing()) { - return; - } - if (targetPart.getSite() instanceof MultiPageEditorSite multiEditorSite) { - if (!state && globalActionHandlerDeaction == null) { - globalActionHandlerDeaction = new DeactivateGlobalActionHandlers(multiEditorSite.getActionBars()); - } else if (state && globalActionHandlerDeaction != null) { - globalActionHandlerDeaction.reactivate(); - globalActionHandlerDeaction = null; - } - } - try { - Method method = AbstractTextEditor.class.getDeclaredMethod("setActionActivation", boolean.class); //$NON-NLS-1$ - method.setAccessible(true); - method.invoke(targetPart, Boolean.valueOf(state)); - } catch (IllegalArgumentException | ReflectiveOperationException ex) { - ILog.of(FindReplaceOverlay.class).error("cannot (de-)activate actions for text editor", ex); //$NON-NLS-1$ - } + commandSupport.overlayDeactivated(); } - - static final class DeactivateGlobalActionHandlers { - private final static List ACTIONS = List.of(ITextEditorActionConstants.CUT, - ITextEditorActionConstants.COPY, ITextEditorActionConstants.PASTE, - ITextEditorActionConstants.DELETE, ITextEditorActionConstants.SELECT_ALL, - ITextEditorActionConstants.FIND); - - private final Map deactivatedActions = new HashMap<>(); - private final IActionBars actionBars; - - public DeactivateGlobalActionHandlers(IActionBars actionBars) { - this.actionBars = actionBars; - for (String actionID : ACTIONS) { - deactivatedActions.putIfAbsent(actionID, actionBars.getGlobalActionHandler(actionID)); - actionBars.setGlobalActionHandler(actionID, null); - } - } - - public void reactivate() { - for (String actionID : deactivatedActions.keySet()) { - actionBars.setGlobalActionHandler(actionID, deactivatedActions.get(actionID)); - } - } - } - }; private final CustomFocusOrder customFocusOrder = new CustomFocusOrder(); @@ -301,6 +240,7 @@ void dispose() { public FindReplaceOverlay(Shell parent, IWorkbenchPart part, IFindReplaceTarget target) { targetPart = part; + commandSupport = new FindReplaceOverlayCommandSupport(targetPart); targetControl = getTargetControl(parent, part); findReplaceLogic = createFindReplaceLogic(target); createContainerAndSearchControls(targetControl); diff --git a/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/overlay/FindReplaceOverlayCommandSupport.java b/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/overlay/FindReplaceOverlayCommandSupport.java new file mode 100644 index 00000000000..50ed200703b --- /dev/null +++ b/bundles/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/findandreplace/overlay/FindReplaceOverlayCommandSupport.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (c) 2026 Vector Informatik GmbH and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.ui.internal.findandreplace.overlay; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.ILog; + +import org.eclipse.jface.action.IAction; + +import org.eclipse.ui.IActionBars; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.part.MultiPageEditorSite; + +import org.eclipse.ui.texteditor.AbstractTextEditor; +import org.eclipse.ui.texteditor.ITextEditorActionConstants; + +/** + * Owns the Find/Replace overlay's command-related infrastructure. Currently + * encapsulates the workaround that disables the target text editor's global + * action handlers while the overlay has focus, preventing them from consuming + * key events that are meant for the overlay's text fields. + */ +class FindReplaceOverlayCommandSupport { + + private final IWorkbenchPart targetPart; + private DeactivateGlobalActionHandlers globalActionHandlerDeaction; + + FindReplaceOverlayCommandSupport(IWorkbenchPart targetPart) { + this.targetPart = targetPart; + } + + void overlayActivated() { + setTextEditorActionsActivated(false); + } + + void overlayDeactivated() { + setTextEditorActionsActivated(true); + } + + /* + * Adapted from + * org.eclipse.jdt.internal.ui.javaeditor.JavaEditor#setActionsActivated(boolean) + */ + private void setTextEditorActionsActivated(boolean state) { + if (!(targetPart instanceof AbstractTextEditor) || targetPart.getSite().getWorkbenchWindow().isClosing()) { + return; + } + if (targetPart.getSite() instanceof MultiPageEditorSite multiEditorSite) { + if (!state && globalActionHandlerDeaction == null) { + globalActionHandlerDeaction = new DeactivateGlobalActionHandlers(multiEditorSite.getActionBars()); + } else if (state && globalActionHandlerDeaction != null) { + globalActionHandlerDeaction.reactivate(); + globalActionHandlerDeaction = null; + } + } + try { + Method method = AbstractTextEditor.class.getDeclaredMethod("setActionActivation", boolean.class); //$NON-NLS-1$ + method.setAccessible(true); + method.invoke(targetPart, Boolean.valueOf(state)); + } catch (IllegalArgumentException | ReflectiveOperationException ex) { + ILog.of(FindReplaceOverlayCommandSupport.class).error("cannot (de-)activate actions for text editor", ex); //$NON-NLS-1$ + } + } + + private static final class DeactivateGlobalActionHandlers { + private static final List ACTIONS = List.of(ITextEditorActionConstants.CUT, + ITextEditorActionConstants.COPY, ITextEditorActionConstants.PASTE, + ITextEditorActionConstants.DELETE, ITextEditorActionConstants.SELECT_ALL, + ITextEditorActionConstants.FIND); + + private final Map deactivatedActions = new HashMap<>(); + private final IActionBars actionBars; + + DeactivateGlobalActionHandlers(IActionBars actionBars) { + this.actionBars = actionBars; + for (String actionID : ACTIONS) { + deactivatedActions.putIfAbsent(actionID, actionBars.getGlobalActionHandler(actionID)); + actionBars.setGlobalActionHandler(actionID, null); + } + } + + void reactivate() { + for (String actionID : deactivatedActions.keySet()) { + actionBars.setGlobalActionHandler(actionID, deactivatedActions.get(actionID)); + } + } + } + +}