1
0
mirror of https://github.com/chylex/IntelliJ-Keyboard-Master.git synced 2024-10-17 07:42:48 +02:00

Compare commits

..

4 Commits

5 changed files with 60 additions and 15 deletions

View File

@ -8,7 +8,7 @@ plugins {
} }
group = "com.chylex.intellij.keyboardmaster" group = "com.chylex.intellij.keyboardmaster"
version = "0.5.4" version = "0.5.5"
repositories { repositories {
mavenCentral() mavenCentral()

View File

@ -8,7 +8,7 @@ import com.intellij.ui.popup.list.ListPopupImpl
internal object PopupInterceptor : PersistentUiInterceptor<AbstractPopup>(AbstractPopup::class.java) { internal object PopupInterceptor : PersistentUiInterceptor<AbstractPopup>(AbstractPopup::class.java) {
override fun shouldIntercept(component: AbstractPopup): Boolean { override fun shouldIntercept(component: AbstractPopup): Boolean {
if (component is ListPopupImpl) { if (component is ListPopupImpl && VimNavigation.isEnabled) {
VimListNavigation.install(component.list, component) VimListNavigation.install(component.list, component)
} }

View File

@ -18,9 +18,12 @@ import javax.swing.KeyStroke
import javax.swing.UIManager import javax.swing.UIManager
object VimNavigation { object VimNavigation {
private val isEnabled = AtomicBoolean(false) private val isEnabledFlag = AtomicBoolean(false)
private var originalPopupBindings: Array<*>? = null private var originalPopupBindings: Array<*>? = null
val isEnabled
get() = isEnabledFlag.get()
fun register() { fun register() {
val disposable = ApplicationManager.getApplication().getService(PluginDisposableService::class.java) val disposable = ApplicationManager.getApplication().getService(PluginDisposableService::class.java)
@ -29,7 +32,7 @@ object VimNavigation {
} }
fun setEnabled(enabled: Boolean) { fun setEnabled(enabled: Boolean) {
if (!isEnabled.compareAndSet(!enabled, enabled)) { if (!isEnabledFlag.compareAndSet(!enabled, enabled)) {
return return
} }
@ -61,7 +64,7 @@ object VimNavigation {
} }
private fun handleEvent(event: AWTEvent) { private fun handleEvent(event: AWTEvent) {
if (event is FocusEvent && event.id == FocusEvent.FOCUS_GAINED && isEnabled.get()) { if (event is FocusEvent && event.id == FocusEvent.FOCUS_GAINED && isEnabled) {
when (val source = event.source) { when (val source = event.source) {
is JList<*> -> VimListNavigation.install(source) is JList<*> -> VimListNavigation.install(source)
is JTree -> VimTreeNavigation.install(source) is JTree -> VimTreeNavigation.install(source)

View File

@ -1,6 +1,7 @@
package com.chylex.intellij.keyboardmaster.feature.vimNavigation package com.chylex.intellij.keyboardmaster.feature.vimNavigation
import com.chylex.intellij.keyboardmaster.PluginDisposableService import com.chylex.intellij.keyboardmaster.PluginDisposableService
import com.chylex.intellij.keyboardmaster.feature.vimNavigation.VimNavigationDispatcher.WrappedAction.ForKeyListener
import com.intellij.openapi.actionSystem.ActionUpdateThread import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.AnActionEvent
@ -20,15 +21,18 @@ import java.awt.event.ActionListener
import java.awt.event.KeyEvent import java.awt.event.KeyEvent
import java.beans.PropertyChangeEvent import java.beans.PropertyChangeEvent
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
import javax.swing.Action
import javax.swing.JComponent import javax.swing.JComponent
import javax.swing.KeyStroke 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 fun findOriginalEnterAction(component: JComponent): WrappedAction? { @JvmStatic
protected val ENTER_KEY: KeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)
private fun findOriginalEnterAction(component: JComponent): WrappedAction {
var originalEnterAction: WrappedAction? = null var originalEnterAction: WrappedAction? = null
for (container in JBIterable.generate<Container>(component) { it.parent }) { for (container in JBIterable.generate<Container>(component) { it.parent }) {
@ -36,7 +40,7 @@ internal open class VimNavigationDispatcher<T : JComponent>(final override val c
continue continue
} }
container.getActionForKeyStroke(ENTER_KEY)?.let { container.getActionForKeyStroke(ENTER_KEY)?.takeUnless(::isIgnoredEnterAction)?.let {
originalEnterAction = WrappedAction.ForActionListener(container, it) originalEnterAction = WrappedAction.ForActionListener(container, it)
} }
@ -47,7 +51,11 @@ internal open class VimNavigationDispatcher<T : JComponent>(final override val c
} }
} }
return originalEnterAction return originalEnterAction ?: ForKeyListener(component)
}
private fun isIgnoredEnterAction(action: ActionListener): Boolean {
return action is Action && action.getValue(Action.NAME) == "toggle"
} }
@Suppress("UnstableApiUsage") @Suppress("UnstableApiUsage")
@ -100,15 +108,23 @@ internal open class VimNavigationDispatcher<T : JComponent>(final override val c
} }
private fun handleEnterKeyPress(actionEvent: AnActionEvent, keyEvent: KeyEvent) { private fun handleEnterKeyPress(actionEvent: AnActionEvent, keyEvent: KeyEvent) {
if (isSearching.compareAndSet(true, false)) { handleEnterKeyPress { originalEnterAction.perform(actionEvent, keyEvent) }
when (val supply = SpeedSearchSupply.getSupply(component)) {
is SpeedSearchBase<*> -> supply.hidePopup()
is SpeedSearch -> supply.reset()
} }
protected inline fun handleEnterKeyPress(originalAction: () -> Unit) {
if (isSearching.compareAndSet(true, false)) {
stopSpeedSearch()
} }
else { else {
currentNode = rootNode currentNode = rootNode
originalEnterAction?.perform(actionEvent, keyEvent) originalAction()
}
}
protected open fun stopSpeedSearch() {
when (val supply = SpeedSearchSupply.getSupply(component)) {
is SpeedSearchBase<*> -> supply.hidePopup()
is SpeedSearch -> supply.reset()
} }
} }
@ -132,7 +148,7 @@ internal open class VimNavigationDispatcher<T : JComponent>(final override val c
private sealed interface WrappedAction { private sealed interface WrappedAction {
fun perform(actionEvent: AnActionEvent, keyEvent: KeyEvent) fun perform(actionEvent: AnActionEvent, keyEvent: KeyEvent)
class ForActionListener(val component: JComponent, val listener: ActionListener) : WrappedAction { class ForActionListener(private val component: JComponent, private val listener: ActionListener) : WrappedAction {
override fun perform(actionEvent: AnActionEvent, keyEvent: KeyEvent) { override fun perform(actionEvent: AnActionEvent, keyEvent: KeyEvent) {
listener.actionPerformed(ActionEvent(component, ActionEvent.ACTION_PERFORMED, "Enter", keyEvent.`when`, keyEvent.modifiersEx)) listener.actionPerformed(ActionEvent(component, ActionEvent.ACTION_PERFORMED, "Enter", keyEvent.`when`, keyEvent.modifiersEx))
} }
@ -143,5 +159,12 @@ internal open class VimNavigationDispatcher<T : JComponent>(final override val c
action.actionPerformed(actionEvent) action.actionPerformed(actionEvent)
} }
} }
class ForKeyListener(private val component: JComponent) : WrappedAction {
override fun perform(actionEvent: AnActionEvent, keyEvent: KeyEvent) {
val unconsumedKeyEvent = KeyEvent(component, keyEvent.id, keyEvent.`when`, keyEvent.modifiersEx, keyEvent.keyCode, keyEvent.keyChar, keyEvent.keyLocation)
component.dispatchEvent(unconsumedKeyEvent)
}
}
} }
} }

View File

@ -8,6 +8,7 @@ import com.intellij.openapi.ui.getUserData
import com.intellij.openapi.ui.putUserData import com.intellij.openapi.ui.putUserData
import com.intellij.openapi.util.Key import com.intellij.openapi.util.Key
import com.intellij.ui.popup.WizardPopup import com.intellij.ui.popup.WizardPopup
import com.intellij.ui.popup.list.ListPopupImpl
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 java.awt.event.ActionEvent import java.awt.event.ActionEvent
@ -85,6 +86,24 @@ internal object VimListNavigation {
// WizardPopup only checks key codes against its input map, but key codes may be undefined for some characters. // WizardPopup only checks key codes against its input map, but key codes may be undefined for some characters.
popup.registerAction("KeyboardMaster-VimListNavigation-PauseSpeedSearch", KeyStroke.getKeyStroke(KeyEvent.CHAR_UNDEFINED, 0), pauseAction) popup.registerAction("KeyboardMaster-VimListNavigation-PauseSpeedSearch", KeyStroke.getKeyStroke(KeyEvent.CHAR_UNDEFINED, 0), pauseAction)
popup.registerAction("KeyboardMaster-VimListNavigation-PauseSpeedSearch", KeyStroke.getKeyStroke(KeyEvent.CHAR_UNDEFINED, KeyEvent.SHIFT_DOWN_MASK), pauseAction) popup.registerAction("KeyboardMaster-VimListNavigation-PauseSpeedSearch", KeyStroke.getKeyStroke(KeyEvent.CHAR_UNDEFINED, KeyEvent.SHIFT_DOWN_MASK), pauseAction)
if (popup is ListPopupImpl) {
popup.registerAction("KeyboardMaster-VimListNavigation-Enter", ENTER_KEY, object : AbstractAction() {
override fun actionPerformed(e: ActionEvent) {
handleEnterKeyPress { popup.handleSelect(true, createEnterEvent(e)) }
}
private fun createEnterEvent(e: ActionEvent): KeyEvent {
return KeyEvent(component, KeyEvent.KEY_PRESSED, e.`when`, e.modifiers, KeyEvent.VK_ENTER, KeyEvent.CHAR_UNDEFINED)
}
})
}
}
override fun stopSpeedSearch() {
val selectedValue = component.selectedValue
super.stopSpeedSearch()
component.setSelectedValue(selectedValue, true)
} }
private class PauseSpeedSearchAction(private val dispatcher: VimNavigationDispatcher<JList<*>>, private val speedSearch: SpeedSearch) : AbstractAction() { private class PauseSpeedSearchAction(private val dispatcher: VimNavigationDispatcher<JList<*>>, private val speedSearch: SpeedSearch) : AbstractAction() {