From a2573e94a5676dc045defa866097d8fbad8bbc39 Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Thu, 12 Jul 2018 20:10:06 +0300 Subject: [PATCH] IDEA-183801: force request focus in StructureView by mouse pressed --- .../StructureViewComponent.java | 9 ++++++ .../actionSystem/impl/ActionButton.java | 2 ++ .../keymap/impl/IdeMouseEventDispatcher.java | 31 +++++++++++++++++++ .../util/src/com/intellij/util/ui/UIUtil.java | 11 +++++-- 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/ide/structureView/newStructureView/StructureViewComponent.java b/platform/lang-impl/src/com/intellij/ide/structureView/newStructureView/StructureViewComponent.java index 33145abb0ee1..692198acc4bf 100644 --- a/platform/lang-impl/src/com/intellij/ide/structureView/newStructureView/StructureViewComponent.java +++ b/platform/lang-impl/src/com/intellij/ide/structureView/newStructureView/StructureViewComponent.java @@ -64,6 +64,7 @@ import javax.swing.tree.TreeSelectionModel; import java.awt.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -71,6 +72,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import static com.intellij.openapi.keymap.impl.IdeMouseEventDispatcher.requestFocusInNonFocusedWindow; + public class StructureViewComponent extends SimpleToolWindowPanel implements TreeActionsOwner, DataProvider, StructureView.Scrollable { private static final Logger LOG = Logger.getInstance("#com.intellij.ide.structureView.newStructureView.StructureViewComponent"); @@ -852,6 +855,12 @@ public class StructureViewComponent extends SimpleToolWindowPanel implements Tre public String getPlace() { return ActionPlaces.STRUCTURE_VIEW_TOOLBAR; } + + @Override + public void processMouseEvent(MouseEvent event) { + requestFocusInNonFocusedWindow(event); + super.processMouseEvent(event); + } } private static class MyPsiTreeChangeListener extends PsiTreeChangeAdapter { diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionButton.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionButton.java index c25a30eb0894..d9323bc8b0f3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionButton.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionButton.java @@ -33,6 +33,7 @@ import java.awt.event.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import static com.intellij.openapi.keymap.impl.IdeMouseEventDispatcher.requestFocusInNonFocusedWindow; import static java.awt.event.KeyEvent.VK_SPACE; public class ActionButton extends JComponent implements ActionButtonComponent, AnActionHolder, Accessible { @@ -360,6 +361,7 @@ public class ActionButton extends JComponent implements ActionButtonComponent, A } protected void processMouseEvent(MouseEvent e) { + requestFocusInNonFocusedWindow(e); super.processMouseEvent(e); if (e.isConsumed()) return; boolean skipPress = checkSkipPressForEvent(e); diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeMouseEventDispatcher.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeMouseEventDispatcher.java index 09020fb702ca..6c7313296f65 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeMouseEventDispatcher.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeMouseEventDispatcher.java @@ -419,4 +419,35 @@ public final class IdeMouseEventDispatcher { blockMode = mode; } } + + public static void requestFocusInNonFocusedWindow(@NotNull MouseEvent event) { + if (event.getID() == MOUSE_PRESSED) { + // request focus by mouse pressed before it settles down + requestFocusInNonFocusedWindow(event.getComponent()); + } + } + + private static void requestFocusInNonFocusedWindow(@Nullable Component component) { + Window window = UIUtil.getWindow(component); + if (window != null && !UIUtil.isFocusAncestor(window)) { + Component focusable = UIUtil.isFocusable(component) ? component : findDefaultFocusableComponent(component); + if (focusable != null) focusable.requestFocus(); + } + } + + @Nullable + private static Component findDefaultFocusableComponent(@Nullable Component component) { + Container provider = findFocusTraversalPolicyProvider(component); + return provider == null ? null : provider.getFocusTraversalPolicy().getDefaultComponent(provider); + } + + @Nullable + private static Container findFocusTraversalPolicyProvider(@Nullable Component component) { + Container container = component == null || component instanceof Container ? (Container)component : component.getParent(); + while (container != null) { + if (container.isFocusCycleRoot() && container.isFocusTraversalPolicyProvider()) return container; + container = container.getParent(); + } + return null; + } } diff --git a/platform/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java index 58fdd579561c..0282b2162ae7 100644 --- a/platform/util/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/src/com/intellij/util/ui/UIUtil.java @@ -2556,8 +2556,13 @@ public class UIUtil { drawCenteredString(g, rect, str, true, true); } - public static boolean isFocusAncestor(@NotNull final JComponent component) { - final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); + /** + * @param component to check whether it has focus within its component hierarchy + * @return {@code true} if component or one of its children has focus + * @see Component#isFocusOwner() + */ + public static boolean isFocusAncestor(@NotNull Component component) { + Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); if (owner == null) return false; if (owner == component) return true; return SwingUtilities.isDescendingFrom(owner, component); @@ -2691,7 +2696,7 @@ public class UIUtil { * @return {@code true} if component is not {@code null} and can be focused * @see Component#isRequestFocusAccepted(boolean, boolean, sun.awt.CausedFocusEvent.Cause) */ - public static boolean isFocusable(JComponent component) { + public static boolean isFocusable(@Nullable Component component) { return component != null && component.isFocusable() && component.isEnabled() && component.isShowing(); }