feat: improved keyPair comboBox (#92)

This commit is contained in:
hstyi
2025-01-16 18:18:58 +08:00
committed by GitHub
parent 992015c8e5
commit 63b27a2f83
4 changed files with 84 additions and 30 deletions

View File

@@ -1,8 +1,5 @@
package app.termora package app.termora
import app.termora.keymgr.KeyManager
import app.termora.keymgr.OhKeyPair
class EditHostOptionsPane(private val host: Host) : HostOptionsPane() { class EditHostOptionsPane(private val host: Host) : HostOptionsPane() {
init { init {
generalOption.portTextField.value = host.port generalOption.portTextField.value = host.port
@@ -10,15 +7,12 @@ class EditHostOptionsPane(private val host: Host) : HostOptionsPane() {
generalOption.protocolTypeComboBox.selectedItem = host.protocol generalOption.protocolTypeComboBox.selectedItem = host.protocol
generalOption.usernameTextField.text = host.username generalOption.usernameTextField.text = host.username
generalOption.hostTextField.text = host.host generalOption.hostTextField.text = host.host
generalOption.passwordTextField.text = host.authentication.password
generalOption.remarkTextArea.text = host.remark generalOption.remarkTextArea.text = host.remark
generalOption.authenticationTypeComboBox.selectedItem = host.authentication.type generalOption.authenticationTypeComboBox.selectedItem = host.authentication.type
if (host.authentication.type == AuthenticationType.PublicKey) { if (host.authentication.type == AuthenticationType.Password) {
val ohKeyPair = KeyManager.getInstance().getOhKeyPair(host.authentication.password) generalOption.passwordTextField.text = host.authentication.password
if (ohKeyPair != null) { } else if (host.authentication.type == AuthenticationType.PublicKey) {
generalOption.publicKeyTextField.text = ohKeyPair.name generalOption.publicKeyComboBox.selectedItem = host.authentication.password
generalOption.publicKeyTextField.putClientProperty(OhKeyPair::class, ohKeyPair)
}
} }
proxyOption.proxyTypeComboBox.selectedItem = host.proxy.type proxyOption.proxyTypeComboBox.selectedItem = host.proxy.type

View File

@@ -1,7 +1,7 @@
package app.termora package app.termora
import app.termora.keymgr.KeyManager
import app.termora.keymgr.KeyManagerDialog import app.termora.keymgr.KeyManagerDialog
import app.termora.keymgr.OhKeyPair
import com.formdev.flatlaf.FlatClientProperties import com.formdev.flatlaf.FlatClientProperties
import com.formdev.flatlaf.extras.components.FlatComboBox import com.formdev.flatlaf.extras.components.FlatComboBox
import com.formdev.flatlaf.ui.FlatTextBorder import com.formdev.flatlaf.ui.FlatTextBorder
@@ -49,10 +49,9 @@ open class HostOptionsPane : OptionsPane() {
password = String(generalOption.passwordTextField.password) password = String(generalOption.passwordTextField.password)
) )
} else if (generalOption.authenticationTypeComboBox.selectedItem == AuthenticationType.PublicKey) { } else if (generalOption.authenticationTypeComboBox.selectedItem == AuthenticationType.PublicKey) {
val keyPair = generalOption.publicKeyTextField.getClientProperty(OhKeyPair::class) as OhKeyPair?
authentication = authentication.copy( authentication = authentication.copy(
type = AuthenticationType.PublicKey, type = AuthenticationType.PublicKey,
password = keyPair?.id ?: StringUtils.EMPTY password = generalOption.publicKeyComboBox.selectedItem?.toString() ?: StringUtils.EMPTY
) )
} }
@@ -111,7 +110,7 @@ open class HostOptionsPane : OptionsPane() {
return false return false
} }
} else if (host.authentication.type == AuthenticationType.PublicKey) { } else if (host.authentication.type == AuthenticationType.PublicKey) {
if (validateField(generalOption.publicKeyTextField)) { if (validateField(generalOption.publicKeyComboBox)) {
return false return false
} }
} }
@@ -149,6 +148,19 @@ open class HostOptionsPane : OptionsPane() {
return false return false
} }
/**
* 返回 true 表示有错误
*/
private fun validateField(comboBox: JComboBox<*>): Boolean {
if (comboBox.isEnabled && comboBox.selectedItem == null) {
selectOptionJComponent(comboBox)
comboBox.putClientProperty(FlatClientProperties.OUTLINE, FlatClientProperties.OUTLINE_ERROR)
comboBox.requestFocusInWindow()
return true
}
return false
}
protected inner class GeneralOption : JPanel(BorderLayout()), Option { protected inner class GeneralOption : JPanel(BorderLayout()), Option {
val portTextField = PortSpinner() val portTextField = PortSpinner()
val nameTextField = OutlineTextField(128) val nameTextField = OutlineTextField(128)
@@ -158,7 +170,7 @@ open class HostOptionsPane : OptionsPane() {
private val passwordPanel = JPanel(BorderLayout()) private val passwordPanel = JPanel(BorderLayout())
private val chooseKeyBtn = JButton(Icons.greyKey) private val chooseKeyBtn = JButton(Icons.greyKey)
val passwordTextField = OutlinePasswordField(255) val passwordTextField = OutlinePasswordField(255)
val publicKeyTextField = OutlineTextField() val publicKeyComboBox = OutlineComboBox<String>()
val remarkTextArea = FixedLengthTextArea(512) val remarkTextArea = FixedLengthTextArea(512)
val authenticationTypeComboBox = FlatComboBox<AuthenticationType>() val authenticationTypeComboBox = FlatComboBox<AuthenticationType>()
@@ -170,7 +182,7 @@ open class HostOptionsPane : OptionsPane() {
private fun initView() { private fun initView() {
add(getCenterComponent(), BorderLayout.CENTER) add(getCenterComponent(), BorderLayout.CENTER)
publicKeyTextField.isEditable = false publicKeyComboBox.isEditable = false
chooseKeyBtn.isFocusable = false chooseKeyBtn.isFocusable = false
protocolTypeComboBox.renderer = object : DefaultListCellRenderer() { protocolTypeComboBox.renderer = object : DefaultListCellRenderer() {
@@ -191,6 +203,28 @@ open class HostOptionsPane : OptionsPane() {
} }
} }
publicKeyComboBox.renderer = object : DefaultListCellRenderer() {
override fun getListCellRendererComponent(
list: JList<*>?,
value: Any?,
index: Int,
isSelected: Boolean,
cellHasFocus: Boolean
): Component {
var text = StringUtils.EMPTY
if (value is String) {
text = KeyManager.getInstance().getOhKeyPair(value)?.name ?: text
}
return super.getListCellRendererComponent(
list,
text,
index,
isSelected,
cellHasFocus
)
}
}
authenticationTypeComboBox.renderer = object : DefaultListCellRenderer() { authenticationTypeComboBox.renderer = object : DefaultListCellRenderer() {
override fun getListCellRendererComponent( override fun getListCellRendererComponent(
list: JList<*>?, list: JList<*>?,
@@ -269,14 +303,20 @@ open class HostOptionsPane : OptionsPane() {
dialog.pack() dialog.pack()
dialog.setLocationRelativeTo(null) dialog.setLocationRelativeTo(null)
dialog.isVisible = true dialog.isVisible = true
if (dialog.ok) {
val lastKeyPair = dialog.getLasOhKeyPair() val selectedItem = publicKeyComboBox.selectedItem
if (lastKeyPair != null) {
publicKeyTextField.putClientProperty(OhKeyPair::class, lastKeyPair) publicKeyComboBox.removeAllItems()
publicKeyTextField.text = lastKeyPair.name for (keyPair in KeyManager.getInstance().getOhKeyPairs()) {
publicKeyTextField.outline = null publicKeyComboBox.addItem(keyPair.id)
}
} }
publicKeyComboBox.selectedItem = selectedItem
if (!dialog.ok) {
return
}
publicKeyComboBox.selectedItem = dialog.getLasOhKeyPair()?.id ?: return
} }
private fun refreshStates() { private fun refreshStates() {
@@ -284,6 +324,7 @@ open class HostOptionsPane : OptionsPane() {
portTextField.isEnabled = true portTextField.isEnabled = true
usernameTextField.isEnabled = true usernameTextField.isEnabled = true
authenticationTypeComboBox.isEnabled = true authenticationTypeComboBox.isEnabled = true
publicKeyComboBox.isEnabled = true
passwordTextField.isEnabled = true passwordTextField.isEnabled = true
chooseKeyBtn.isEnabled = true chooseKeyBtn.isEnabled = true
@@ -293,6 +334,7 @@ open class HostOptionsPane : OptionsPane() {
usernameTextField.isEnabled = false usernameTextField.isEnabled = false
authenticationTypeComboBox.isEnabled = false authenticationTypeComboBox.isEnabled = false
passwordTextField.isEnabled = false passwordTextField.isEnabled = false
publicKeyComboBox.isEnabled = false
chooseKeyBtn.isEnabled = false chooseKeyBtn.isEnabled = false
} }
@@ -369,10 +411,16 @@ open class HostOptionsPane : OptionsPane() {
passwordPanel.removeAll() passwordPanel.removeAll()
if (authenticationTypeComboBox.selectedItem == AuthenticationType.PublicKey) { if (authenticationTypeComboBox.selectedItem == AuthenticationType.PublicKey) {
val selectedItem = publicKeyComboBox.selectedItem
publicKeyComboBox.removeAllItems()
for (pair in KeyManager.getInstance().getOhKeyPairs()) {
publicKeyComboBox.addItem(pair.id)
}
publicKeyComboBox.selectedItem = selectedItem
passwordPanel.add( passwordPanel.add(
FormBuilder.create() FormBuilder.create()
.layout(FormLayout("default:grow, 4dlu, left:pref", "pref")) .layout(FormLayout("default:grow, 4dlu, left:pref", "pref"))
.add(publicKeyTextField).xy(1, 1) .add(publicKeyComboBox).xy(1, 1)
.add(chooseKeyBtn).xy(3, 1) .add(chooseKeyBtn).xy(3, 1)
.build(), BorderLayout.CENTER .build(), BorderLayout.CENTER
) )

View File

@@ -522,13 +522,18 @@ class HostTree : JTree(), Disposable {
while (parents.isNotEmpty()) { while (parents.isNotEmpty()) {
val p = parents.removeFirst() val p = parents.removeFirst()
for (i in 0 until model.getChildCount(p)) { for (i in 0 until getModel().getChildCount(p)) {
val child = model.getChild(p, i) as Host val child = getModel().getChild(p, i) as Host
nodes.add(child) nodes.add(child)
parents.add(child) parents.add(child)
} }
} }
// 确保是最新的
for (i in 0 until nodes.size) {
nodes[i] = model.getHost(nodes[i].id) ?: continue
}
return nodes return nodes
} }

View File

@@ -1,13 +1,11 @@
package app.termora package app.termora
import com.formdev.flatlaf.FlatClientProperties
import com.formdev.flatlaf.extras.components.* import com.formdev.flatlaf.extras.components.*
import com.formdev.flatlaf.ui.FlatTextBorder import com.formdev.flatlaf.ui.FlatTextBorder
import org.apache.commons.lang3.StringUtils import org.apache.commons.lang3.StringUtils
import java.awt.Component import java.awt.Component
import java.awt.event.FocusAdapter import java.awt.event.*
import java.awt.event.FocusEvent
import java.awt.event.KeyAdapter
import java.awt.event.KeyEvent
import java.text.ParseException import java.text.ParseException
import javax.swing.DefaultListCellRenderer import javax.swing.DefaultListCellRenderer
import javax.swing.JComboBox import javax.swing.JComboBox
@@ -53,6 +51,15 @@ class OutlineTextArea : FlatTextArea() {
} }
} }
class OutlineComboBox<T> : JComboBox<T>() {
init {
addItemListener {
if (it.stateChange == ItemEvent.SELECTED) {
putClientProperty(FlatClientProperties.OUTLINE, null)
}
}
}
}
class FixedLengthTextArea(var maxLength: Int = Int.MAX_VALUE) : FlatTextArea() { class FixedLengthTextArea(var maxLength: Int = Int.MAX_VALUE) : FlatTextArea() {
init { init {