Using a LanguageTextField in a Preferences dialog - Tab key issue

Completed

I'm creating a plugin where some of the settings will be handled by entering JSON. I'm using a LanguageTextField, and it's working fairly well, but I can't use the Tab key for indenting text - the Tab key ends up functioning for navigation instead.

I tried doing this:

newJsonConfig.focusTraversalKeysEnabled = false

...but that had no effect. Any suggestions about what would work?

0
2 comments

I came up with a solution, but I sure hope that there's something easier than this. Solving the problem required setting focusTraversalKeysEnabled on an inner component of the LanguageTextField, then adding a key listener to that component to handle indentation via keyboard events.

What's odd is that one form of indentation - forward indentation of a selection - was automatically handled as soon as focusTraversalKeysEnabled was false. But forward indentation from an insert cursor was not handled, nor any form of unindenting.

The code below was needed to solve other problems too, like that fact that some of the configuration of a LanguageTextField can't be done as soon as you create an instance - you have to wait until the component has been displayed before it has an editor to accept some of the settings, like isLineNumbersShown, and the focusTraversalKeysEnabled attribute of the content of the editor.

package com.shetline.json

import com.intellij.ide.DataManager
import com.intellij.json.json5.Json5Language
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.editor.actionSystem.EditorAction
import com.intellij.openapi.editor.actions.IndentSelectionAction
import com.intellij.openapi.editor.actions.UnindentSelectionAction
import com.intellij.openapi.editor.colors.EditorColorsManager
import com.intellij.openapi.editor.ex.EditorEx
import com.intellij.openapi.project.Project
import com.intellij.ui.LanguageTextField
import java.awt.Font
import java.awt.KeyboardFocusManager
import java.awt.event.ComponentAdapter
import java.awt.event.ComponentEvent
import java.awt.event.KeyAdapter
import java.awt.event.KeyEvent
import javax.swing.JComponent
import javax.swing.KeyStroke

class JsonEditor(project: Project, value: String? = ""):
LanguageTextField(Json5Language.INSTANCE, project, value ?: "") {
init {
val scheme = EditorColorsManager.getInstance().globalScheme

font = Font(scheme.editorFontName, Font.PLAIN, scheme.editorFontSize)
isOneLineMode = false
autoscrolls = true
addComponentListener(MyComponentAdapter())
}

inner class MyComponentAdapter : ComponentAdapter() {
private var initDone = false

override fun componentResized(e: ComponentEvent?) {
componentShown(e)
}

override fun componentShown(e: ComponentEvent?) {
val editor = editor as? EditorEx

if (editor == null || initDone)
return

val content = editor.contentComponent
val indentKeys = getKeys(content, KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, "pressed TAB")
val unindentKeys = getKeys(content, KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, "shift pressed TAB")

editor.settings.isLineNumbersShown = true
content.focusTraversalKeysEnabled = false
content.addKeyListener(object: KeyAdapter() {
override fun keyPressed(event: KeyEvent?) {
val keyStroke = if (event == null) null else KeyStroke.getKeyStrokeForEvent(event).toString()

if (indentKeys.contains(keyStroke))
performAction(editor, IndentSelectionAction())
else if (unindentKeys.contains(keyStroke))
performAction(editor, UnindentSelectionAction())
}
})
editor.setHorizontalScrollbarVisible(true)
editor.setVerticalScrollbarVisible(true)

initDone = true
}
}

private fun getKeys(component: JComponent, whichKeys: Int, default: String): List<String> {
val keys = component.getFocusTraversalKeys(whichKeys)?.map { key -> key.toString() }

return if (keys == null || keys.isEmpty()) listOf(default) else keys
}

private fun performAction(editor: EditorEx, action: EditorAction) {
for (caret in editor.caretModel.allCarets) {
WriteCommandAction.runWriteCommandAction(editor.project) {
action.handler.execute(editor, caret, DataManager.getInstance().getDataContext(editor.contentComponent))
}
}
}
}
0

I had the same problem and wondered how the debugger does it. Apparently by not using LanguageTextField:

class CustomLanguageTextField(
    project: Project,
    language: Language,
    text: String,
) : EditorTextField(
    SimpleDocumentCreator().createDocument(text, language, project),
    project,
    language.associatedFileType,
    false,
    false
) {
    override fun createEditor(): EditorEx {
        val editor = super.createEditor()
        editor.setHorizontalScrollbarVisible(true)
        editor.setVerticalScrollbarVisible(true)
        editor.settings.isUseSoftWraps = false
        editor.settings.lineCursorWidth = EditorUtil.getDefaultCaretWidth()
        editor.colorsScheme.editorFontName = font.fontName
        editor.colorsScheme.editorFontSize = font.size
        editor.contentComponent.border =
            CompoundBorder(editor.contentComponent.border, JBUI.Borders.emptyLeft(2))
        return editor
    }
}
0

Please sign in to leave a comment.