1
0
mirror of https://github.com/chylex/IntelliJ-Keyboard-Master.git synced 2025-05-01 17:34:08 +02:00

Fix more cases of vim-style navigation breaking Enter actions

This commit is contained in:
chylex 2024-05-06 21:02:39 +02:00
parent 215049bf26
commit 2b7f1a363f
Signed by: chylex
GPG Key ID: 4DE42C8F19A80548

View File

@ -2,13 +2,18 @@ package com.chylex.intellij.keyboardmaster.feature.vimNavigation
import com.chylex.intellij.keyboardmaster.PluginDisposableService import com.chylex.intellij.keyboardmaster.PluginDisposableService
import com.intellij.openapi.actionSystem.ActionUpdateThread import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.KeyboardShortcut
import com.intellij.openapi.actionSystem.ex.ActionUtil
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.DumbAwareAction import com.intellij.openapi.project.DumbAwareAction
import com.intellij.toolWindow.InternalDecoratorImpl import com.intellij.toolWindow.InternalDecoratorImpl
import com.intellij.ui.SpeedSearchBase import com.intellij.ui.SpeedSearchBase
import com.intellij.ui.speedSearch.SpeedSearch import com.intellij.ui.speedSearch.SpeedSearch
import com.intellij.ui.speedSearch.SpeedSearchSupply import com.intellij.ui.speedSearch.SpeedSearchSupply
import com.intellij.util.containers.JBIterable
import java.awt.Container
import java.awt.event.ActionEvent import java.awt.event.ActionEvent
import java.awt.event.ActionListener import java.awt.event.ActionListener
import java.awt.event.KeyEvent import java.awt.event.KeyEvent
@ -19,18 +24,42 @@ import javax.swing.KeyStroke
internal open class VimNavigationDispatcher<T : JComponent>(final override val component: T, private val rootNode: KeyStrokeNode.Parent<VimNavigationDispatcher<T>>) : DumbAwareAction(), ComponentHolder { internal open class VimNavigationDispatcher<T : JComponent>(final override val component: T, private val rootNode: KeyStrokeNode.Parent<VimNavigationDispatcher<T>>) : DumbAwareAction(), ComponentHolder {
companion object { companion object {
private val DISPOSABLE = ApplicationManager.getApplication().getService(PluginDisposableService::class.java) private val DISPOSABLE = ApplicationManager.getApplication().getService(PluginDisposableService::class.java)
private val ENTER_KEY = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)
private val EXTRA_SHORTCUTS = setOf( private val EXTRA_SHORTCUTS = setOf(
KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0) ENTER_KEY
) )
private fun findOriginalEnterAction(component: JComponent): WrappedAction? {
var originalEnterAction: WrappedAction? = null
for (container in JBIterable.generate<Container>(component) { it.parent }) {
if (container !is JComponent) {
continue
}
container.getActionForKeyStroke(ENTER_KEY)?.let {
originalEnterAction = WrappedAction.ForActionListener(component, it)
}
for (action in ActionUtil.getActions(container)) {
if (action.shortcutSet.shortcuts.any { it is KeyboardShortcut && it.firstKeyStroke == ENTER_KEY && it.secondKeyStroke == null }) {
originalEnterAction = WrappedAction.ForAnAction(action)
}
}
}
return originalEnterAction
}
@Suppress("UnstableApiUsage") @Suppress("UnstableApiUsage")
fun JComponent.getParentToolWindowId(): String? { fun JComponent.getParentToolWindowId(): String? {
return InternalDecoratorImpl.findNearestDecorator(this)?.toolWindowId return InternalDecoratorImpl.findNearestDecorator(this)?.toolWindowId
} }
} }
private val originalEnterAction: ActionListener? = component.getActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)) private val originalEnterAction = findOriginalEnterAction(component)
private var currentNode: KeyStrokeNode.Parent<VimNavigationDispatcher<T>> = rootNode private var currentNode: KeyStrokeNode.Parent<VimNavigationDispatcher<T>> = rootNode
var isSearching = AtomicBoolean(false) var isSearching = AtomicBoolean(false)
@ -52,7 +81,7 @@ internal open class VimNavigationDispatcher<T : JComponent>(final override val c
final override fun actionPerformed(e: AnActionEvent) { final override fun actionPerformed(e: AnActionEvent) {
val keyEvent = e.inputEvent as? KeyEvent ?: return val keyEvent = e.inputEvent as? KeyEvent ?: return
if (keyEvent.id == KeyEvent.KEY_PRESSED && handleSpecialKeyPress(keyEvent)) { if (keyEvent.id == KeyEvent.KEY_PRESSED && handleSpecialKeyPress(e, keyEvent)) {
currentNode = rootNode currentNode = rootNode
return return
} }
@ -66,20 +95,20 @@ internal open class VimNavigationDispatcher<T : JComponent>(final override val c
} }
} }
private fun handleSpecialKeyPress(keyEvent: KeyEvent): Boolean { private fun handleSpecialKeyPress(actionEvent: AnActionEvent, keyEvent: KeyEvent): Boolean {
if (keyEvent.keyCode == KeyEvent.VK_ESCAPE) { if (keyEvent.keyCode == KeyEvent.VK_ESCAPE) {
return true return true
} }
if (keyEvent.keyCode == KeyEvent.VK_ENTER) { if (keyEvent.keyCode == KeyEvent.VK_ENTER) {
handleEnterKeyPress(ActionEvent(component, ActionEvent.ACTION_PERFORMED, "Enter", keyEvent.`when`, keyEvent.modifiersEx)) handleEnterKeyPress(actionEvent, keyEvent)
return true return true
} }
return false return false
} }
private fun handleEnterKeyPress(e: ActionEvent) { private fun handleEnterKeyPress(actionEvent: AnActionEvent, keyEvent: KeyEvent) {
if (isSearching.compareAndSet(true, false)) { if (isSearching.compareAndSet(true, false)) {
when (val supply = SpeedSearchSupply.getSupply(component)) { when (val supply = SpeedSearchSupply.getSupply(component)) {
is SpeedSearchBase<*> -> supply.hidePopup() is SpeedSearchBase<*> -> supply.hidePopup()
@ -87,7 +116,7 @@ internal open class VimNavigationDispatcher<T : JComponent>(final override val c
} }
} }
else { else {
originalEnterAction?.actionPerformed(e) originalEnterAction?.perform(actionEvent, keyEvent)
} }
} }
@ -98,4 +127,20 @@ internal open class VimNavigationDispatcher<T : JComponent>(final override val c
final override fun getActionUpdateThread(): ActionUpdateThread { final override fun getActionUpdateThread(): ActionUpdateThread {
return ActionUpdateThread.BGT return ActionUpdateThread.BGT
} }
private sealed interface WrappedAction {
fun perform(actionEvent: AnActionEvent, keyEvent: KeyEvent)
class ForActionListener(val component: JComponent, val listener: ActionListener) : WrappedAction {
override fun perform(actionEvent: AnActionEvent, keyEvent: KeyEvent) {
listener.actionPerformed(ActionEvent(component, ActionEvent.ACTION_PERFORMED, "Enter", keyEvent.`when`, keyEvent.modifiersEx))
}
}
class ForAnAction(val action: AnAction) : WrappedAction {
override fun perform(actionEvent: AnActionEvent, keyEvent: KeyEvent) {
action.actionPerformed(actionEvent)
}
}
}
} }