IDEA-229505 Focus is not returned to Editor from Tool window in Float or Window view mode by Esc button

GitOrigin-RevId: d5aee42851410aee608387cd43edb129d1e3b2e8
This commit is contained in:
Vladimir Krivosheev
2019-12-19 17:35:54 +00:00
committed by intellij-monorepo-bot
parent 53c9d5b93f
commit 8a2dc52e4b
11 changed files with 140 additions and 214 deletions
@@ -1,4 +1,4 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
/*
* @author Eugene Zhuravlev
@@ -23,12 +23,13 @@ import com.intellij.xdebugger.impl.actions.XDebuggerActions;
import com.intellij.xdebugger.impl.ui.DebuggerUIUtil;
import com.sun.jdi.VMDisconnectedException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
public abstract class DebuggerTreePanel extends UpdatableDebuggerView implements DataProvider, Disposable {
public static final DataKey<DebuggerTreePanel> DATA_KEY = DataKey.create("DebuggerPanel");
private final SingleAlarm myRebuildAlarm = new SingleAlarm(() -> {
try {
final DebuggerContextImpl context = getContext();
@@ -59,6 +60,12 @@ public abstract class DebuggerTreePanel extends UpdatableDebuggerView implements
myTree.addMouseListener(popupHandler);
setFocusTraversalPolicy(new IdeFocusTraversalPolicy() {
@Nullable
@Override
protected Project getProject() {
return project;
}
@Override
public Component getDefaultComponent(Container focusCycleRoot) {
return myTree;
@@ -1009,6 +1009,12 @@ public abstract class DiffRequestProcessor implements Disposable {
if (component == null) return null;
return IdeFocusTraversalPolicy.getPreferredFocusedComponent(component, this);
}
@Nullable
@Override
protected Project getProject() {
return myProject;
}
}
private class MyDiffContext extends DiffContextEx {
@@ -546,6 +546,12 @@ public abstract class MergeRequestProcessor implements Disposable {
if (component == null) return null;
return IdeFocusTraversalPolicy.getPreferredFocusedComponent(component, this);
}
@Nullable
@Override
protected Project getProject() {
return myProject;
}
}
private class MyDiffContext extends MergeContextEx {
@@ -10,7 +10,7 @@ import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.openapi.util.ExpirableRunnable;
import com.intellij.util.ui.UIUtil;
import com.intellij.ui.ComponentUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -166,18 +166,15 @@ public abstract class IdeFocusManager implements FocusRequestor {
}
@NotNull
public static IdeFocusManager findInstanceByComponent(@NotNull Component c) {
final IdeFocusManager instance = findByComponent(c);
return instance != null ? instance : findInstanceByContext(null);
public static IdeFocusManager findInstanceByComponent(@NotNull Component component) {
IdeFocusManager instance = findByComponent(component);
return instance == null ? findInstanceByContext(null) : instance;
}
@Nullable
private static IdeFocusManager findByComponent(Component c) {
final Component parent = UIUtil.findUltimateParent(c);
if (parent instanceof IdeFrame) {
return getInstanceSafe(((IdeFrame)parent).getProject());
}
return null;
private static IdeFocusManager findByComponent(@NotNull Component component) {
Component parent = ComponentUtil.findUltimateParent(component);
return parent instanceof IdeFrame ? getInstanceSafe(((IdeFrame)parent).getProject()) : null;
}
@@ -18,19 +18,6 @@ abstract class ToolWindowManager {
@JvmStatic
fun getInstance(project: Project): ToolWindowManager = project.getService(ToolWindowManager::class.java)
@JvmStatic
fun getActiveToolWindow(): ToolWindow? {
val frame = IdeFocusManager.getGlobalInstance().lastFocusedFrame
val project = frame?.project
if (project == null || project.isDisposed || project.isDefault) {
return null
}
val toolWindowManager = getInstance(project)
val activeId = toolWindowManager.activeToolWindowId
return activeId?.let { toolWindowManager.getToolWindow(it) }
}
@JvmStatic
fun getActiveId(): String? {
val project = IdeFocusManager.getGlobalInstance().lastFocusedFrame?.project ?: return null
@@ -112,8 +99,6 @@ abstract class ToolWindowManager {
@Deprecated("Use ToolWindowFactory and toolWindow extension point")
abstract fun unregisterToolWindow(id: String)
/**
*/
abstract fun activateEditorComponent()
/**
@@ -557,6 +557,12 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener
}
return IdeFocusTraversalPolicy.getPreferredFocusedComponent(EditorsSplitters.this, this);
}
@NotNull
@Override
protected Project getProject() {
return myManager.getProject();
}
}
@Nullable
@@ -964,16 +970,17 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener
}
@Nullable
private static EditorsSplitters getSplittersToFocus() {
private static EditorsSplitters getSplittersToFocus(@Nullable Project project) {
Window activeWindow = WindowManagerEx.getInstanceEx().getMostRecentFocusedWindow();
if (activeWindow instanceof FloatingDecorator) {
IdeFocusManager ideFocusManager = IdeFocusManager.findInstanceByComponent(activeWindow);
IdeFrame lastFocusedFrame = ideFocusManager.getLastFocusedFrame();
IdeFrame lastFocusedFrame = IdeFocusManager.findInstanceByComponent(activeWindow).getLastFocusedFrame();
JComponent frameComponent = lastFocusedFrame != null ? lastFocusedFrame.getComponent() : null;
Window lastFocusedWindow = frameComponent != null ? SwingUtilities.getWindowAncestor(frameComponent) : null;
Window lastFocusedWindow = frameComponent == null ? null : SwingUtilities.getWindowAncestor(frameComponent);
activeWindow = ObjectUtils.notNull(lastFocusedWindow, activeWindow);
Project project = lastFocusedFrame == null ? null : lastFocusedFrame.getProject();
if (project == null) {
project = lastFocusedFrame == null ? null : lastFocusedFrame.getProject();
}
FileEditorManagerEx fileEditorManager = project == null || project.isDisposed() ? null : FileEditorManagerEx.getInstanceEx(project);
if (fileEditorManager == null) {
return null;
@@ -983,7 +990,9 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener
}
if (activeWindow instanceof IdeFrame.Child) {
Project project = ((IdeFrame.Child)activeWindow).getProject();
if (project == null) {
project = ((IdeFrame.Child)activeWindow).getProject();
}
return getSplittersForProject(WindowManager.getInstance().getFrame(project), project);
}
@@ -1006,8 +1015,8 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener
}
@Nullable
public static JComponent findDefaultComponentInSplitters() {
EditorsSplitters splittersToFocus = getSplittersToFocus();
public static JComponent findDefaultComponentInSplitters(@Nullable Project project) {
EditorsSplitters splittersToFocus = getSplittersToFocus(project);
if (splittersToFocus == null) {
return null;
}
@@ -1023,10 +1032,11 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener
return null;
}
public static boolean focusDefaultComponentInSplittersIfPresent() {
JComponent defaultFocusedComponentInEditor = findDefaultComponentInSplitters();
public static boolean focusDefaultComponentInSplittersIfPresent(@NotNull Project project) {
JComponent defaultFocusedComponentInEditor = findDefaultComponentInSplitters(project);
if (defaultFocusedComponentInEditor != null) {
defaultFocusedComponentInEditor.requestFocusInWindow();
// not requestFocus because if floating or windowed tool window is deactivated (or, ESC pressed to focus editor), then we should focus our window
defaultFocusedComponentInEditor.requestFocus();
return true;
}
return false;
@@ -54,7 +54,6 @@ import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
import com.intellij.openapi.vfs.newvfs.events.VFileMoveEvent;
import com.intellij.openapi.vfs.newvfs.events.VFilePropertyChangeEvent;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.openapi.wm.ex.StatusBarEx;
import com.intellij.reference.SoftReference;
@@ -902,7 +901,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Persis
Window windowAncestor = SwingUtilities.getWindowAncestor(window.myPanel);
if (windowAncestor != null &&
windowAncestor.equals(KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow())) {
ToolWindowManager.getInstance(myProject).activateEditorComponent();
EditorsSplitters.focusDefaultComponentInSplittersIfPresent(myProject);
IdeFocusManager.getInstance(myProject).toFront(window.getOwner());
}
}
@@ -1458,23 +1457,21 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Persis
return;
}
ToolWindowManager.getInstance(myProject).invokeLater(() -> {
if (!myProject.isDisposed()) {
CommandProcessor.getInstance().executeCommand(myProject, () -> {
ApplicationManager.getApplication().invokeLater(() -> {
long currentTime = System.nanoTime();
Long startTime = myProject.getUserData(ProjectImpl.CREATION_TIME);
if (startTime != null) {
long time = (currentTime - startTime.longValue()) / 1000000;
LifecycleUsageTriggerCollector.onProjectOpenFinished(myProject, time);
ApplicationManager.getApplication().invokeLater(() -> {
CommandProcessor.getInstance().executeCommand(myProject, () -> {
ApplicationManager.getApplication().invokeLater(() -> {
long currentTime = System.nanoTime();
Long startTime = myProject.getUserData(ProjectImpl.CREATION_TIME);
if (startTime != null) {
long time = (currentTime - startTime.longValue()) / 1000000;
LifecycleUsageTriggerCollector.onProjectOpenFinished(myProject, time);
LOG.info("Project opening took " + time + " ms");
}
}, myProject.getDisposed());
// group 1
}, "", null);
}
});
LOG.info("Project opening took " + time + " ms");
}
}, myProject.getDisposed());
// group 1
}, "", null);
}, myProject.getDisposed());
});
}
@@ -1602,9 +1599,10 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Persis
final FileEditor selectedEditor = editor.getSelectedEditor();
for (int i = editors.length - 1; i >= 0; i--) {
final FileEditor editor1 = editors[i];
final FileEditorProvider provider = providers[i];
if (!editor.equals(selectedEditor)) { // we already notified the myEditor (when fire event)
FileEditor editor1 = editors[i];
FileEditorProvider provider = providers[i];
if (!editor.equals(selectedEditor)) {
// we already notified the myEditor (when fire event)
if (selectedEditor.equals(editor1)) {
editor1.deselectNotify();
}
@@ -4,6 +4,7 @@ package com.intellij.openapi.wm.ex;
import com.intellij.openapi.editor.impl.EditorComponentImpl;
import com.intellij.openapi.fileEditor.impl.EditorWindowHolder;
import com.intellij.openapi.fileEditor.impl.EditorsSplitters;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -30,30 +31,34 @@ public class IdeFocusTraversalPolicy extends LayoutFocusTraversalPolicy {
@Override
public Component getComponentAfter(Container aContainer, Component aComponent) {
Component after = super.getComponentAfter(aContainer, aComponent);
Component defaultFocusableComponent = findDefaultFocusedComponentIfSplitters(after);
if (defaultFocusableComponent != null) return defaultFocusableComponent;
if (after != null) return after.isFocusable() ? after : findFocusableComponentIn((JComponent)after, null);
return findFocusableComponentIn(aContainer, aComponent);
return doFind(aContainer, aComponent, after);
}
@Override
public Component getComponentBefore(Container aContainer, Component aComponent) {
Component before = super.getComponentBefore(aContainer, aComponent);
Component defaultFocusableComponent = findDefaultFocusedComponentIfSplitters(before);
if (defaultFocusableComponent != null) return defaultFocusableComponent;
if (before != null) return before.isFocusable() ? before : findFocusableComponentIn((JComponent)before, null);
return findFocusableComponentIn(aContainer, aComponent);
return doFind(aContainer, aComponent, before);
}
@Nullable
private static Component findDefaultFocusedComponentIfSplitters(Component componentToSearchInto) {
if (componentToSearchInto instanceof EditorsSplitters) {
JComponent defaultFocusableComponent = EditorsSplitters.findDefaultComponentInSplitters();
protected Project getProject() {
return null;
}
@Nullable
private Component doFind(Container aContainer, Component aComponent, @Nullable Component siblingComponent) {
if (siblingComponent == null) {
return findFocusableComponentIn(aContainer, aComponent);
}
if (siblingComponent instanceof EditorsSplitters) {
Component defaultFocusableComponent = EditorsSplitters.findDefaultComponentInSplitters(getProject());
if (defaultFocusableComponent != null) {
return defaultFocusableComponent;
}
}
return null;
return siblingComponent.isFocusable() ? siblingComponent : findFocusableComponentIn((JComponent)siblingComponent, null);
}
/**
@@ -141,7 +146,6 @@ public class IdeFocusTraversalPolicy extends LayoutFocusTraversalPolicy {
return false;
}
/* TODO[anton,vova] implement Policy in Editor component instead */
if (component instanceof EditorComponentImpl || component instanceof EditorWindowHolder) {
return true;
}
@@ -70,7 +70,6 @@ import java.beans.PropertyChangeListener
import java.util.*
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicReference
import java.util.function.Predicate
import javax.swing.*
import javax.swing.event.HyperlinkEvent
import javax.swing.event.HyperlinkListener
@@ -167,18 +166,14 @@ open class ToolWindowManagerImpl(val project: Project) : ToolWindowManagerEx(),
}
})
Windows.ToolWindowProvider(Windows.Signal(Predicate { event ->
event.id == FocusEvent.FOCUS_LOST || event.id == FocusEvent.FOCUS_GAINED || event.id == MouseEvent.MOUSE_PRESSED || event.id == KeyEvent.KEY_PRESSED
}))
.withEscAction()
.handleFocusLostOnPinned { toolWindowId ->
addFocusLostListener(pinnedWindowFocusLostHandler = { toolWindowId ->
process { manager ->
val entry = manager.idToEntry.get(toolWindowId) ?: return@process
val info = manager.layout.getInfo(toolWindowId) ?: return@process
manager.doDeactivateToolWindow(info, entry)
}
}
.bind(ApplicationManager.getApplication())
)
connection.subscribe(KeymapManagerListener.TOPIC, object : KeymapManagerListener {
override fun activeKeymapChanged(keymap: Keymap?) {
@@ -500,8 +495,9 @@ open class ToolWindowManagerImpl(val project: Project) : ToolWindowManagerEx(),
}
override fun activateEditorComponent() {
if (!EditorsSplitters.focusDefaultComponentInSplittersIfPresent()) {
frame?.rootPane?.requestFocusInWindow()
if (!EditorsSplitters.focusDefaultComponentInSplittersIfPresent(project)) {
// see note about requestFocus in focusDefaultComponentInSplittersIfPresent
frame?.rootPane?.requestFocus()
}
}
@@ -1907,4 +1903,52 @@ private const val LAYOUT_TO_RESTORE = "layout-to-restore"
internal enum class ToolWindowProperty {
TITLE, ICON, AVAILABLE, STRIPE_TITLE
}
}
private fun addFocusLostListener(pinnedWindowFocusLostHandler: (String) -> Unit) {
val listener = AWTEventListener { event ->
val eventId = event.id
if (event !is FocusEvent || !(eventId == FocusEvent.FOCUS_LOST || eventId == FocusEvent.FOCUS_LOST || eventId == FocusEvent.FOCUS_GAINED)) {
return@AWTEventListener
}
val id = ToolWindowManager.getActiveId() ?: return@AWTEventListener
// let's check that it is a toolwindow who loses the focus
if (event.oppositeComponent != null && isInActiveToolWindow(event.source) && !isInActiveToolWindow(event.oppositeComponent)) {
// a toolwindow lost focus
val activeToolWindow = getActiveToolWindow()
val focusGoesToPopup = JBPopupFactory.getInstance().getParentBalloonFor(event.oppositeComponent) != null
if (!event.isTemporary &&
!focusGoesToPopup && activeToolWindow != null &&
(activeToolWindow.isAutoHide || activeToolWindow.type == ToolWindowType.SLIDING)) {
pinnedWindowFocusLostHandler(id)
}
}
}
Toolkit.getDefaultToolkit().addAWTEventListener(listener, AWTEvent.FOCUS_EVENT_MASK)
}
private fun isInActiveToolWindow(component: Any?): Boolean {
val activeToolWindow = getActiveToolWindow() ?: return false
var source = if (component is JComponent) component else null
val activeToolWindowComponent = activeToolWindow.getComponentIfInitialized()
if (activeToolWindowComponent != null) {
while (source != null && source !== activeToolWindowComponent) {
source = if (source.parent != null && source.parent is JComponent) source.parent as JComponent else null
}
}
return source != null
}
private fun getActiveToolWindow(): ToolWindowImpl? {
val frame = IdeFocusManager.getGlobalInstance().lastFocusedFrame
val project = frame?.project
if (project == null || project.isDisposed || project.isDefault) {
return null
}
val toolWindowManager = ToolWindowManager.getInstance(project)
val activeId = toolWindowManager.activeToolWindowId
return activeId?.let { toolWindowManager.getToolWindow(it) as ToolWindowImpl }
}
@@ -1,117 +0,0 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.openapi.wm.impl;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.openapi.wm.ToolWindowType;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.AWTEventListener;
import java.awt.event.FocusEvent;
import java.util.function.Consumer;
import java.util.function.Predicate;
@ApiStatus.Internal
public final class Windows {
public final static class ToolWindowProvider {
private final Signal mySignal;
private Consumer<? super String> pinnedWindowFocusLostHandler;
public ToolWindowProvider(@NotNull Signal signal) {
mySignal = signal;
}
public ToolWindowProvider handleFocusLostOnPinned(@NotNull Consumer<? super String> value) {
pinnedWindowFocusLostHandler = value;
return this;
}
public ToolWindowProvider withEscAction() {
return this;
}
public static boolean isInActiveToolWindow(@Nullable Object component) {
ToolWindowImpl activeToolWindow = (ToolWindowImpl)ToolWindowManager.getActiveToolWindow();
if (activeToolWindow == null) {
return false;
}
JComponent source = component instanceof JComponent ? (JComponent)component : null;
JComponent activeToolWindowComponent = activeToolWindow.getComponentIfInitialized();
if (activeToolWindowComponent != null) {
while (source != null && source != activeToolWindowComponent) {
source = ((source.getParent() != null) && (source.getParent() instanceof JComponent)) ? ((JComponent)source.getParent()) : null;
}
}
return source != null;
}
public void bind(@NotNull Disposable parentDisposable) {
AWTEventListener listener = new AWTEventListener() {
@Override
public void eventDispatched(AWTEvent event) {
if (!mySignal.isAppropriatePredicate.test(event)) {
return;
}
// Find toolwindows from the event
// pass the toolwindow to the appropriate consumers
// FocusEvent
// for now we are interested in focus lost events
String id = ToolWindowManager.getActiveId();
if (event.getID() != FocusEvent.FOCUS_LOST) {
return;
}
// let's check that it is a toolwindow who loses the focus
FocusEvent focusEvent = (FocusEvent)event;
if (isInActiveToolWindow(focusEvent.getSource())
&& !isInActiveToolWindow(focusEvent.getOppositeComponent())
&& focusEvent.getOppositeComponent() != null) {
//System.err.println("Tool window is loosing focus: " + ToolWindowManager.getActiveToolWindow().getStripeTitle());
// A toolwindow lost focus
ToolWindow activeToolWindow = ToolWindowManager.getActiveToolWindow();
boolean focusGoesToPopup = JBPopupFactory.getInstance().getParentBalloonFor(focusEvent.getOppositeComponent()) != null;
if (!focusEvent.isTemporary() &&
!focusGoesToPopup &&
activeToolWindow != null &&
(activeToolWindow.isAutoHide() || activeToolWindow.getType() == ToolWindowType.SLIDING)) {
pinnedWindowFocusLostHandler.accept(id);
}
}
}
};
Toolkit.getDefaultToolkit().addAWTEventListener(listener, AWTEvent.MOUSE_EVENT_MASK | AWTEvent.FOCUS_EVENT_MASK | AWTEvent.KEY_EVENT_MASK);
Disposer.register(parentDisposable, new Disposable() {
@Override
public void dispose() {
Toolkit.getDefaultToolkit().removeAWTEventListener(listener);
}
});
}
}
static final class Signal {
private final Predicate<? super AWTEvent> isAppropriatePredicate;
Signal(Predicate<? super AWTEvent> isAppropriatePredicate) {
this.isAppropriatePredicate = isAppropriatePredicate;
}
public boolean appropriate(AWTEvent event) {
return isAppropriatePredicate.test(event);
}
}
}
@@ -1,18 +1,4 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ui;
import com.intellij.openapi.Disposable;