chore: improve terminal options

This commit is contained in:
hstyi
2025-07-14 11:02:21 +08:00
committed by hstyi
parent a8a1fea91b
commit 7f1317a9a7
14 changed files with 311 additions and 449 deletions

View File

@@ -4,7 +4,7 @@ plugins {
project.version = "0.0.1"
project.version = "0.0.2"
dependencies {

View File

@@ -1,7 +1,9 @@
package app.termora.plugins.serial
import app.termora.*
import app.termora.plugin.internal.AltKeyModifier
import app.termora.plugin.internal.BasicGeneralOption
import app.termora.plugin.internal.BasicTerminalOption
import com.fazecast.jSerialComm.SerialPort
import com.formdev.flatlaf.FlatClientProperties
import com.jgoodies.forms.builder.FormBuilder
@@ -15,12 +17,15 @@ import java.awt.BorderLayout
import java.awt.Component
import java.awt.event.ComponentAdapter
import java.awt.event.ComponentEvent
import java.nio.charset.Charset
import javax.swing.*
class SerialHostOptionsPane : OptionsPane() {
private val generalOption = BasicGeneralOption()
private val terminalOption = TerminalOption()
private val terminalOption = BasicTerminalOption().apply {
showCharsetComboBox = true
showStartupCommandTextField = true
init()
}
private val serialCommOption = SerialCommOption()
init {
@@ -48,6 +53,10 @@ class SerialHostOptionsPane : OptionsPane() {
encoding = terminalOption.charsetComboBox.selectedItem as String,
startupCommand = terminalOption.startupCommandTextField.text,
serialComm = serialComm,
extras = mutableMapOf(
"altModifier" to (terminalOption.altModifierComboBox.selectedItem?.toString()
?: AltKeyModifier.EightBit.name),
)
)
return Host(
@@ -128,67 +137,6 @@ class SerialHostOptionsPane : OptionsPane() {
}
protected inner class TerminalOption : JPanel(BorderLayout()), Option {
val charsetComboBox = JComboBox<String>()
val startupCommandTextField = OutlineTextField()
init {
initView()
initEvents()
}
private fun initView() {
add(getCenterComponent(), BorderLayout.CENTER)
for (e in Charset.availableCharsets()) {
charsetComboBox.addItem(e.key)
}
charsetComboBox.selectedItem = "UTF-8"
}
private fun initEvents() {
}
override fun getIcon(isSelected: Boolean): Icon {
return Icons.terminal
}
override fun getTitle(): String {
return I18n.getString("termora.new-host.terminal")
}
override fun getJComponent(): JComponent {
return this
}
private fun getCenterComponent(): JComponent {
val layout = FormLayout(
"left:pref, $FORM_MARGIN, default:grow",
"pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref"
)
var rows = 1
val step = 2
val panel = FormBuilder.create().layout(layout)
.add("${I18n.getString("termora.new-host.terminal.encoding")}:").xy(1, rows)
.add(charsetComboBox).xy(3, rows).apply { rows += step }
.add("${I18n.getString("termora.new-host.terminal.startup-commands")}:").xy(1, rows)
.add(startupCommandTextField).xy(3, rows).apply { rows += step }
.apply { rows += step }
.build()
return panel
}
}
protected inner class SerialCommOption : JPanel(BorderLayout()), Option {
val serialPortComboBox = OutlineComboBox<String>()
val baudRateComboBox = OutlineComboBox<Int>()

View File

@@ -1,6 +1,7 @@
package app.termora
import app.termora.actions.DataProviders
import app.termora.plugin.internal.AltKeyModifier
import app.termora.terminal.*
import kotlinx.coroutines.*
import kotlinx.coroutines.swing.Swing
@@ -46,6 +47,9 @@ abstract class PtyHostTerminalTab(
// 开启 reader
startPtyConnectorReader()
// 修饰
terminalKeyModifiers()
// 启动命令
if (host.options.startupCommand.isNotBlank()) {
coroutineScope.launch(Dispatchers.IO) {
@@ -155,6 +159,15 @@ abstract class PtyHostTerminalTab(
ptyConnector.write(bytes)
}
open fun terminalKeyModifiers() {
val altModifier = host.options.extras["altModifier"]
if (altModifier == AltKeyModifier.CharactersPrecededByESC.name) {
terminalModel.setData(DataKey.AltModifier, AltKeyModifier.CharactersPrecededByESC)
} else {
terminalModel.setData(DataKey.AltModifier, AltKeyModifier.EightBit)
}
}
override fun canReconnect(): Boolean {
return true
}

View File

@@ -0,0 +1,6 @@
package app.termora.plugin.internal
enum class AltKeyModifier {
EightBit,
CharactersPrecededByESC,
}

View File

@@ -0,0 +1,191 @@
package app.termora.plugin.internal
import app.termora.*
import app.termora.OptionsPane.Companion.FORM_MARGIN
import app.termora.OptionsPane.Option
import app.termora.plugin.internal.telnet.TelnetHostOptionsPane.Backspace
import com.formdev.flatlaf.extras.components.FlatTabbedPane
import com.formdev.flatlaf.ui.FlatTextBorder
import com.jgoodies.forms.builder.FormBuilder
import com.jgoodies.forms.layout.FormLayout
import java.awt.BorderLayout
import java.awt.Component
import java.awt.KeyboardFocusManager
import java.nio.charset.Charset
import javax.swing.*
class BasicTerminalOption() : JPanel(BorderLayout()), Option {
var showCharsetComboBox: Boolean = false
var showStartupCommandTextField: Boolean = false
var showHeartbeatIntervalTextField: Boolean = false
var showEnvironmentTextArea: Boolean = false
var showLoginScripts: Boolean = false
var showBackspaceComboBox: Boolean = false
var showCharacterAtATimeTextField: Boolean = false
var showAltModifierComboBox: Boolean = true
val charsetComboBox = JComboBox<String>()
val startupCommandTextField = OutlineTextField()
val heartbeatIntervalTextField = IntSpinner(30, minimum = 3, maximum = Int.MAX_VALUE)
val environmentTextArea = FixedLengthTextArea(2048)
val loginScripts = mutableListOf<LoginScript>()
val backspaceComboBox = JComboBox<Backspace>()
val altModifierComboBox = JComboBox<AltKeyModifier>()
val characterAtATimeTextField = YesOrNoComboBox()
private val loginScriptPanel = LoginScriptPanel(loginScripts)
private val tabbed = FlatTabbedPane()
fun init() {
initView()
initEvents()
}
private fun initView() {
if (showLoginScripts) {
tabbed.styleMap = mapOf(
"focusColor" to DynamicColor("TabbedPane.background"),
"hoverColor" to DynamicColor("TabbedPane.background"),
)
tabbed.tabHeight = UIManager.getInt("TabbedPane.tabHeight") - 4
putClientProperty("ContentPanelBorder", BorderFactory.createEmptyBorder())
tabbed.addTab(I18n.getString("termora.new-host.general"), getCenterComponent())
tabbed.addTab(I18n.getString("termora.new-host.terminal.login-scripts"), loginScriptPanel)
add(tabbed, BorderLayout.CENTER)
} else {
add(getCenterComponent(), BorderLayout.CENTER)
}
if (showAltModifierComboBox) {
altModifierComboBox.addItem(AltKeyModifier.EightBit)
altModifierComboBox.addItem(AltKeyModifier.CharactersPrecededByESC)
altModifierComboBox.renderer = object : DefaultListCellRenderer() {
override fun getListCellRendererComponent(
list: JList<*>?,
value: Any?,
index: Int,
isSelected: Boolean,
cellHasFocus: Boolean
): Component? {
var text = value?.toString() ?: value
if (value == AltKeyModifier.CharactersPrecededByESC) {
text = I18n.getString("termora.new-host.terminal.alt-modifier.by-esc")
} else if (value == AltKeyModifier.EightBit) {
text = I18n.getString("termora.new-host.terminal.alt-modifier.eight-bit")
}
return super.getListCellRendererComponent(list, text, index, isSelected, cellHasFocus)
}
}
}
if (showBackspaceComboBox) {
backspaceComboBox.addItem(Backspace.Delete)
backspaceComboBox.addItem(Backspace.Backspace)
backspaceComboBox.addItem(Backspace.VT220)
}
if (showCharacterAtATimeTextField) {
characterAtATimeTextField.selectedItem = false
}
environmentTextArea.setFocusTraversalKeys(
KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.getDefaultFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS)
)
environmentTextArea.setFocusTraversalKeys(
KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.getDefaultFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS)
)
environmentTextArea.rows = 8
environmentTextArea.lineWrap = true
environmentTextArea.border = BorderFactory.createEmptyBorder(4, 4, 4, 4)
for (e in Charset.availableCharsets()) {
charsetComboBox.addItem(e.key)
}
charsetComboBox.selectedItem = "UTF-8"
}
private fun initEvents() {
}
override fun getIcon(isSelected: Boolean): Icon {
return Icons.terminal
}
override fun getTitle(): String {
return I18n.getString("termora.new-host.terminal")
}
override fun getJComponent(): JComponent {
return this
}
private fun getCenterComponent(): JComponent {
val layout = FormLayout(
"left:pref, $FORM_MARGIN, default:grow",
"pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref"
)
var rows = 1
val step = 2
val builder = FormBuilder.create().layout(layout)
if (showLoginScripts) {
builder.border(BorderFactory.createEmptyBorder(6, 8, 6, 8))
}
if (showCharsetComboBox) {
builder.add("${I18n.getString("termora.new-host.terminal.encoding")}:").xy(1, rows)
.add(charsetComboBox).xy(3, rows).apply { rows += step }
}
if (showAltModifierComboBox) {
builder.add("${I18n.getString("termora.new-host.terminal.alt-modifier")}:").xy(1, rows)
.add(altModifierComboBox).xy(3, rows).apply { rows += step }
}
if (showBackspaceComboBox) {
builder.add("${I18n.getString("termora.new-host.terminal.backspace")}:").xy(1, rows)
.add(backspaceComboBox).xy(3, rows).apply { rows += step }
}
if (showCharacterAtATimeTextField) {
builder
.add("${I18n.getString("termora.new-host.terminal.character-mode")}:").xy(1, rows)
.add(characterAtATimeTextField).xy(3, rows).apply { rows += step }
}
if (showHeartbeatIntervalTextField) {
builder.add("${I18n.getString("termora.new-host.terminal.heartbeat-interval")}:").xy(1, rows)
.add(heartbeatIntervalTextField).xy(3, rows).apply { rows += step }
}
if (showStartupCommandTextField) {
builder.add("${I18n.getString("termora.new-host.terminal.startup-commands")}:").xy(1, rows)
.add(startupCommandTextField).xy(3, rows).apply { rows += step }
}
if (showEnvironmentTextArea) {
builder.add("${I18n.getString("termora.new-host.terminal.env")}:").xy(1, rows)
.add(JScrollPane(environmentTextArea).apply { border = FlatTextBorder() }).xy(3, rows)
.apply { rows += step }
}
return builder.build()
}
}

View File

@@ -1,20 +1,25 @@
package app.termora.plugin.internal.local
import app.termora.*
import app.termora.Host
import app.termora.Options
import app.termora.OptionsPane
import app.termora.SerialComm
import app.termora.plugin.internal.AltKeyModifier
import app.termora.plugin.internal.BasicGeneralOption
import app.termora.plugin.internal.BasicTerminalOption
import com.formdev.flatlaf.FlatClientProperties
import com.formdev.flatlaf.ui.FlatTextBorder
import com.jgoodies.forms.builder.FormBuilder
import com.jgoodies.forms.layout.FormLayout
import java.awt.BorderLayout
import java.awt.KeyboardFocusManager
import java.awt.Window
import java.nio.charset.Charset
import javax.swing.*
import javax.swing.JTextField
import javax.swing.SwingUtilities
internal open class LocalHostOptionsPane : OptionsPane() {
protected val generalOption = BasicGeneralOption()
protected val terminalOption = TerminalOption()
private val terminalOption = BasicTerminalOption().apply {
showCharsetComboBox = true
showEnvironmentTextArea = true
showStartupCommandTextField = true
init()
}
protected val owner: Window get() = SwingUtilities.getWindowAncestor(this)
init {
@@ -35,6 +40,10 @@ internal open class LocalHostOptionsPane : OptionsPane() {
env = terminalOption.environmentTextArea.text,
startupCommand = terminalOption.startupCommandTextField.text,
serialComm = serialComm,
extras = mutableMapOf(
"altModifier" to (terminalOption.altModifierComboBox.selectedItem?.toString()
?: AltKeyModifier.EightBit.name),
)
)
return Host(
@@ -77,83 +86,4 @@ internal open class LocalHostOptionsPane : OptionsPane() {
textField.requestFocusInWindow()
}
protected inner class TerminalOption : JPanel(BorderLayout()), Option {
val charsetComboBox = JComboBox<String>()
val startupCommandTextField = OutlineTextField()
val environmentTextArea = FixedLengthTextArea(2048)
init {
initView()
initEvents()
}
private fun initView() {
add(getCenterComponent(), BorderLayout.CENTER)
environmentTextArea.setFocusTraversalKeys(
KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.getDefaultFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS)
)
environmentTextArea.setFocusTraversalKeys(
KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.getDefaultFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS)
)
environmentTextArea.rows = 8
environmentTextArea.lineWrap = true
environmentTextArea.border = BorderFactory.createEmptyBorder(4, 4, 4, 4)
for (e in Charset.availableCharsets()) {
charsetComboBox.addItem(e.key)
}
charsetComboBox.selectedItem = "UTF-8"
}
private fun initEvents() {
}
override fun getIcon(isSelected: Boolean): Icon {
return Icons.terminal
}
override fun getTitle(): String {
return I18n.getString("termora.new-host.terminal")
}
override fun getJComponent(): JComponent {
return this
}
private fun getCenterComponent(): JComponent {
val layout = FormLayout(
"left:pref, $FORM_MARGIN, default:grow",
"pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref"
)
var rows = 1
val step = 2
val panel = FormBuilder.create().layout(layout)
.add("${I18n.getString("termora.new-host.terminal.encoding")}:").xy(1, rows)
.add(charsetComboBox).xy(3, rows).apply { rows += step }
.add("${I18n.getString("termora.new-host.terminal.startup-commands")}:").xy(1, rows)
.add(startupCommandTextField).xy(3, rows).apply { rows += step }
.add("${I18n.getString("termora.new-host.terminal.env")}:").xy(1, rows)
.add(JScrollPane(environmentTextArea).apply { border = FlatTextBorder() }).xy(3, rows)
.apply { rows += step }
.build()
return panel
}
}
}

View File

@@ -4,13 +4,14 @@ import app.termora.*
import app.termora.account.AccountOwner
import app.termora.keymgr.KeyManager
import app.termora.keymgr.KeyManagerDialog
import app.termora.plugin.internal.AltKeyModifier
import app.termora.plugin.internal.BasicProxyOption
import app.termora.plugin.internal.BasicTerminalOption
import app.termora.tree.Filter
import app.termora.tree.HostTreeNode
import app.termora.tree.NewHostTreeDialog
import com.formdev.flatlaf.FlatClientProperties
import com.formdev.flatlaf.extras.components.FlatComboBox
import com.formdev.flatlaf.extras.components.FlatTabbedPane
import com.formdev.flatlaf.ui.FlatTextBorder
import com.formdev.flatlaf.util.SystemInfo
import com.jgoodies.forms.builder.FormBuilder
@@ -21,20 +22,26 @@ import org.eclipse.jgit.internal.transport.sshd.agent.connector.UnixDomainSocket
import org.eclipse.jgit.internal.transport.sshd.agent.connector.WinPipeConnector
import java.awt.*
import java.awt.event.*
import java.nio.charset.Charset
import javax.swing.*
import javax.swing.table.DefaultTableCellRenderer
import javax.swing.table.DefaultTableModel
@Suppress("CascadeIf")
open class SSHHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPane() {
protected val tunnelingOption = TunnelingOption()
protected val generalOption = GeneralOption()
protected val proxyOption = BasicProxyOption()
protected val terminalOption = TerminalOption()
protected val jumpHostsOption = JumpHostsOption()
protected val sftpOption = SFTPOption()
protected val owner: Window get() = SwingUtilities.getWindowAncestor(this)
internal class SSHHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPane() {
private val tunnelingOption = TunnelingOption()
private val generalOption = GeneralOption()
private val proxyOption = BasicProxyOption()
private val terminalOption = BasicTerminalOption().apply {
showCharsetComboBox = true
showLoginScripts = true
showEnvironmentTextArea = true
showStartupCommandTextField = true
showHeartbeatIntervalTextField = true
init()
}
private val jumpHostsOption = JumpHostsOption()
private val sftpOption = SFTPOption()
private val owner: Window get() = SwingUtilities.getWindowAncestor(this)
init {
addOption(generalOption)
@@ -47,7 +54,7 @@ open class SSHHostOptionsPane(private val accountOwner: AccountOwner) : OptionsP
}
open fun getHost(): Host {
fun getHost(): Host {
val name = generalOption.nameTextField.text
val protocol = SSHProtocolProvider.PROTOCOL
val host = generalOption.hostTextField.text
@@ -98,6 +105,10 @@ open class SSHHostOptionsPane(private val accountOwner: AccountOwner) : OptionsP
enableX11Forwarding = tunnelingOption.x11ForwardingCheckBox.isSelected,
x11Forwarding = tunnelingOption.x11ServerTextField.text,
loginScripts = terminalOption.loginScripts,
extras = mutableMapOf(
"altModifier" to (terminalOption.altModifierComboBox.selectedItem?.toString()
?: AltKeyModifier.EightBit.name),
)
)
return Host(
@@ -486,102 +497,6 @@ open class SSHHostOptionsPane(private val accountOwner: AccountOwner) : OptionsP
}
protected inner class TerminalOption : JPanel(BorderLayout()), Option {
val charsetComboBox = JComboBox<String>()
val startupCommandTextField = OutlineTextField()
val heartbeatIntervalTextField = IntSpinner(30, minimum = 3, maximum = Int.MAX_VALUE)
val environmentTextArea = FixedLengthTextArea(2048)
val loginScripts = mutableListOf<LoginScript>()
private val loginScriptPanel = LoginScriptPanel(loginScripts)
private val tabbed = FlatTabbedPane()
init {
initView()
initEvents()
}
private fun initView() {
tabbed.styleMap = mapOf(
"focusColor" to DynamicColor("TabbedPane.background"),
"hoverColor" to DynamicColor("TabbedPane.background"),
)
tabbed.tabHeight = UIManager.getInt("TabbedPane.tabHeight") - 4
putClientProperty("ContentPanelBorder", BorderFactory.createEmptyBorder())
tabbed.addTab(I18n.getString("termora.new-host.general"), getCenterComponent())
tabbed.addTab(I18n.getString("termora.new-host.terminal.login-scripts"), loginScriptPanel)
add(tabbed, BorderLayout.CENTER)
environmentTextArea.setFocusTraversalKeys(
KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.getDefaultFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS)
)
environmentTextArea.setFocusTraversalKeys(
KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.getDefaultFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS)
)
environmentTextArea.rows = 8
environmentTextArea.lineWrap = true
environmentTextArea.border = BorderFactory.createEmptyBorder(4, 4, 4, 4)
for (e in Charset.availableCharsets()) {
charsetComboBox.addItem(e.key)
}
charsetComboBox.selectedItem = "UTF-8"
}
private fun initEvents() {
}
override fun getIcon(isSelected: Boolean): Icon {
return Icons.terminal
}
override fun getTitle(): String {
return I18n.getString("termora.new-host.terminal")
}
override fun getJComponent(): JComponent {
return this
}
private fun getCenterComponent(): JComponent {
val layout = FormLayout(
"left:pref, $FORM_MARGIN, default:grow",
"pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref"
)
var rows = 1
val step = 2
val panel = FormBuilder.create().layout(layout)
.border(BorderFactory.createEmptyBorder(6, 8, 6, 8))
.add("${I18n.getString("termora.new-host.terminal.encoding")}:").xy(1, rows)
.add(charsetComboBox).xy(3, rows).apply { rows += step }
.add("${I18n.getString("termora.new-host.terminal.heartbeat-interval")}:").xy(1, rows)
.add(heartbeatIntervalTextField).xy(3, rows).apply { rows += step }
.add("${I18n.getString("termora.new-host.terminal.startup-commands")}:").xy(1, rows)
.add(startupCommandTextField).xy(3, rows).apply { rows += step }
.add("${I18n.getString("termora.new-host.terminal.env")}:").xy(1, rows)
.add(JScrollPane(environmentTextArea).apply { border = FlatTextBorder() }).xy(3, rows)
.apply { rows += step }
.build()
return panel
}
}
protected inner class SFTPOption : JPanel(BorderLayout()), Option {
val defaultDirectoryField = OutlineTextField(255)

View File

@@ -2,9 +2,10 @@ package app.termora.plugin.internal.telnet
import app.termora.*
import app.termora.account.AccountOwner
import app.termora.plugin.internal.AltKeyModifier
import app.termora.plugin.internal.BasicProxyOption
import app.termora.plugin.internal.BasicTerminalOption
import com.formdev.flatlaf.FlatClientProperties
import com.formdev.flatlaf.extras.components.FlatTabbedPane
import com.formdev.flatlaf.ui.FlatTextBorder
import com.jgoodies.forms.builder.FormBuilder
import com.jgoodies.forms.layout.FormLayout
@@ -12,7 +13,6 @@ import java.awt.BorderLayout
import java.awt.KeyboardFocusManager
import java.awt.event.ComponentAdapter
import java.awt.event.ComponentEvent
import java.nio.charset.Charset
import javax.swing.*
class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPane() {
@@ -20,7 +20,16 @@ class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPan
// telnet 不支持代理密码
private val proxyOption = BasicProxyOption(authenticationTypes = listOf())
private val terminalOption = TerminalOption()
private val terminalOption = BasicTerminalOption().apply {
showCharsetComboBox = true
showBackspaceComboBox = true
showStartupCommandTextField = true
showCharacterAtATimeTextField = true
showEnvironmentTextArea = true
showLoginScripts = true
init()
}
init {
addOption(generalOption)
@@ -58,7 +67,9 @@ class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPan
serialComm = serialComm,
extras = mutableMapOf(
"backspace" to (terminalOption.backspaceComboBox.selectedItem as Backspace).name,
"character-at-a-time" to (terminalOption.characterAtATimeTextField.selectedItem?.toString() ?: "false")
"character-at-a-time" to (terminalOption.characterAtATimeTextField.selectedItem?.toString() ?: "false"),
"altModifier" to (terminalOption.altModifierComboBox.selectedItem?.toString()
?: AltKeyModifier.EightBit.name),
)
)
@@ -226,108 +237,6 @@ class TelnetHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPan
}
private inner class TerminalOption : JPanel(BorderLayout()), Option {
val charsetComboBox = JComboBox<String>()
val backspaceComboBox = JComboBox<Backspace>()
val startupCommandTextField = OutlineTextField()
val characterAtATimeTextField = YesOrNoComboBox()
val environmentTextArea = FixedLengthTextArea(2048)
val loginScripts = mutableListOf<LoginScript>()
private val loginScriptPanel = LoginScriptPanel(loginScripts)
init {
initView()
initEvents()
}
private fun initView() {
backspaceComboBox.addItem(Backspace.Delete)
backspaceComboBox.addItem(Backspace.Backspace)
backspaceComboBox.addItem(Backspace.VT220)
characterAtATimeTextField.selectedItem = false
environmentTextArea.setFocusTraversalKeys(
KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.getDefaultFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS)
)
environmentTextArea.setFocusTraversalKeys(
KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.getDefaultFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS)
)
environmentTextArea.rows = 8
environmentTextArea.lineWrap = true
environmentTextArea.border = BorderFactory.createEmptyBorder(4, 4, 4, 4)
for (e in Charset.availableCharsets()) {
charsetComboBox.addItem(e.key)
}
charsetComboBox.selectedItem = "UTF-8"
val tabbed = FlatTabbedPane()
tabbed.styleMap = mapOf(
"focusColor" to DynamicColor("TabbedPane.background"),
"hoverColor" to DynamicColor("TabbedPane.background"),
)
tabbed.tabHeight = UIManager.getInt("TabbedPane.tabHeight") - 4
putClientProperty("ContentPanelBorder", BorderFactory.createEmptyBorder())
tabbed.addTab(I18n.getString("termora.new-host.general"), getCenterComponent())
tabbed.addTab(I18n.getString("termora.new-host.terminal.login-scripts"), loginScriptPanel)
add(tabbed, BorderLayout.CENTER)
}
private fun initEvents() {
}
override fun getIcon(isSelected: Boolean): Icon {
return Icons.terminal
}
override fun getTitle(): String {
return I18n.getString("termora.new-host.terminal")
}
override fun getJComponent(): JComponent {
return this
}
private fun getCenterComponent(): JComponent {
val layout = FormLayout(
"left:pref, $FORM_MARGIN, default:grow",
"pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref"
)
var rows = 1
val step = 2
val panel = FormBuilder.create().layout(layout)
.border(BorderFactory.createEmptyBorder(6, 8, 6, 8))
.add("${I18n.getString("termora.new-host.terminal.encoding")}:").xy(1, rows)
.add(charsetComboBox).xy(3, rows).apply { rows += step }
.add("${I18n.getString("termora.new-host.terminal.backspace")}:").xy(1, rows)
.add(backspaceComboBox).xy(3, rows).apply { rows += step }
.add("${I18n.getString("termora.new-host.terminal.character-mode")}:").xy(1, rows)
.add(characterAtATimeTextField).xy(3, rows).apply { rows += step }
.add("${I18n.getString("termora.new-host.terminal.startup-commands")}:").xy(1, rows)
.add(startupCommandTextField).xy(3, rows).apply { rows += step }
.add("${I18n.getString("termora.new-host.terminal.env")}:").xy(1, rows)
.add(JScrollPane(environmentTextArea).apply { border = FlatTextBorder() }).xy(3, rows)
.apply { rows += step }
.build()
return panel
}
}
enum class Backspace {
/**
* 0x08

View File

@@ -1,6 +1,8 @@
package app.termora.plugin.internal.wsl
import app.termora.*
import app.termora.plugin.internal.AltKeyModifier
import app.termora.plugin.internal.BasicTerminalOption
import com.formdev.flatlaf.FlatClientProperties
import com.formdev.flatlaf.ui.FlatTextBorder
import com.jgoodies.forms.builder.FormBuilder
@@ -12,12 +14,17 @@ import java.awt.KeyboardFocusManager
import java.awt.Window
import java.awt.event.ComponentAdapter
import java.awt.event.ComponentEvent
import java.nio.charset.Charset
import javax.swing.*
internal open class WSLHostOptionsPane : OptionsPane() {
protected val generalOption = GeneralOption()
protected val terminalOption = TerminalOption()
protected val terminalOption = BasicTerminalOption().apply {
showCharsetComboBox = true
showStartupCommandTextField = true
showEnvironmentTextArea = true
init()
}
protected val owner: Window get() = SwingUtilities.getWindowAncestor(this)
init {
@@ -36,7 +43,11 @@ internal open class WSLHostOptionsPane : OptionsPane() {
encoding = terminalOption.charsetComboBox.selectedItem as String,
env = terminalOption.environmentTextArea.text,
startupCommand = terminalOption.startupCommandTextField.text,
extras = mutableMapOf("wsl-guid" to wsl.guid, "wsl-flavor" to wsl.flavor)
extras = mutableMapOf(
"wsl-guid" to wsl.guid, "wsl-flavor" to wsl.flavor,
"altModifier" to (terminalOption.altModifierComboBox.selectedItem?.toString()
?: AltKeyModifier.EightBit.name),
)
)
return Host(
@@ -216,85 +227,5 @@ internal open class WSLHostOptionsPane : OptionsPane() {
}
protected inner class TerminalOption : JPanel(BorderLayout()), Option {
val charsetComboBox = JComboBox<String>()
val startupCommandTextField = OutlineTextField()
val environmentTextArea = FixedLengthTextArea(2048)
init {
initView()
initEvents()
}
private fun initView() {
add(getCenterComponent(), BorderLayout.CENTER)
startupCommandTextField.placeholderText = "--cd ~"
environmentTextArea.setFocusTraversalKeys(
KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.getDefaultFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS)
)
environmentTextArea.setFocusTraversalKeys(
KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.getDefaultFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS)
)
environmentTextArea.rows = 8
environmentTextArea.lineWrap = true
environmentTextArea.border = BorderFactory.createEmptyBorder(4, 4, 4, 4)
for (e in Charset.availableCharsets()) {
charsetComboBox.addItem(e.key)
}
charsetComboBox.selectedItem = "UTF-8"
}
private fun initEvents() {
}
override fun getIcon(isSelected: Boolean): Icon {
return Icons.terminal
}
override fun getTitle(): String {
return I18n.getString("termora.new-host.terminal")
}
override fun getJComponent(): JComponent {
return this
}
private fun getCenterComponent(): JComponent {
val layout = FormLayout(
"left:pref, $FORM_MARGIN, default:grow",
"pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref"
)
var rows = 1
val step = 2
val panel = FormBuilder.create().layout(layout)
.add("${I18n.getString("termora.new-host.terminal.encoding")}:").xy(1, rows)
.add(charsetComboBox).xy(3, rows).apply { rows += step }
.add("${I18n.getString("termora.new-host.terminal.startup-commands")}:").xy(1, rows)
.add(startupCommandTextField).xy(3, rows).apply { rows += step }
.add("${I18n.getString("termora.new-host.terminal.env")}:").xy(1, rows)
.add(JScrollPane(environmentTextArea).apply { border = FlatTextBorder() }).xy(3, rows)
.apply { rows += step }
.build()
return panel
}
}
}

View File

@@ -1,5 +1,6 @@
package app.termora.terminal
import app.termora.plugin.internal.AltKeyModifier
import kotlin.reflect.KClass
@@ -192,6 +193,11 @@ class DataKey<T : Any>(val clazz: KClass<T>) {
* TerminalWriter
*/
val TerminalWriter = DataKey(app.termora.terminal.panel.TerminalWriter::class)
/**
* [app.termora.plugin.internal.AltKeyModifier]
*/
val AltModifier = DataKey(AltKeyModifier::class)
}
}

View File

@@ -2,7 +2,9 @@ package app.termora.terminal.panel
import app.termora.keymap.KeyShortcut
import app.termora.keymap.KeymapManager
import app.termora.plugin.internal.AltKeyModifier
import app.termora.terminal.ControlCharacters
import app.termora.terminal.DataKey
import app.termora.terminal.Terminal
import com.formdev.flatlaf.util.SystemInfo
import org.slf4j.LoggerFactory
@@ -89,8 +91,10 @@ class TerminalPanelKeyAdapter(
return
}
// https://github.com/TermoraDev/termora/issues/865
val modifier = terminal.getTerminalModel().getData(DataKey.AltModifier, AltKeyModifier.EightBit)
// https://github.com/TermoraDev/termora/issues/331
if (isAltPressedOnly(e) && Character.isDefined(e.keyChar)) {
if (isAltPressedOnly(e) && Character.isDefined(e.keyChar) && modifier == AltKeyModifier.CharactersPrecededByESC) {
val c = String(charArrayOf(ASCII_ESC, simpleMapKeyCodeToChar(e)))
writer.write(TerminalWriter.WriteRequest.fromBytes(c.toByteArray(writer.getCharset())))
// scroll to bottom

View File

@@ -186,6 +186,9 @@ termora.new-host.terminal.backspace=Backspace
termora.new-host.terminal.character-mode=Character-at-a-time
termora.new-host.terminal.heartbeat-interval=Heartbeat Interval
termora.new-host.terminal.startup-commands=Startup Command
termora.new-host.terminal.alt-modifier=Alt modifier
termora.new-host.terminal.alt-modifier.eight-bit=8-bit characters
termora.new-host.terminal.alt-modifier.by-esc=Characters preceded by ESC
termora.new-host.terminal.env=Environment
termora.new-host.terminal.login-scripts=Login Scripts
termora.new-host.terminal.expect=Expect

View File

@@ -178,6 +178,9 @@ termora.new-host.terminal.backspace=退格键
termora.new-host.terminal.character-mode=单字符模式
termora.new-host.terminal.heartbeat-interval=心跳间隔
termora.new-host.terminal.startup-commands=启动命令
termora.new-host.terminal.alt-modifier=Alt 键修饰
termora.new-host.terminal.alt-modifier.eight-bit=8 位字符
termora.new-host.terminal.alt-modifier.by-esc=ESC 键作为前缀
termora.new-host.terminal.env=环境
termora.new-host.terminal.login-scripts=登录脚本
termora.new-host.terminal.expect=预期

View File

@@ -175,6 +175,9 @@ termora.new-host.terminal.encoding=編碼
termora.new-host.terminal.backspace=退格鍵
termora.new-host.terminal.character-mode=單字元模式
termora.new-host.terminal.startup-commands=啟動命令
termora.new-host.terminal.alt-modifier=Alt 鍵修飾
termora.new-host.terminal.alt-modifier.eight-bit=8 位元字符
termora.new-host.terminal.alt-modifier.by-esc=ESC 鍵作為前綴
termora.new-host.terminal.heartbeat-interval=心跳間隔
termora.new-host.terminal.env=環境
termora.new-host.terminal.login-scripts=登入腳本