From 63b27a2f83037f295072d5e35f31062f354ddc54 Mon Sep 17 00:00:00 2001 From: hstyi Date: Thu, 16 Jan 2025 18:18:58 +0800 Subject: [PATCH] feat: improved keyPair comboBox (#92) --- .../kotlin/app/termora/EditHostOptionsPane.kt | 14 +--- .../kotlin/app/termora/HostOptionsPane.kt | 76 +++++++++++++++---- src/main/kotlin/app/termora/HostTree.kt | 9 ++- src/main/kotlin/app/termora/TextField.kt | 15 +++- 4 files changed, 84 insertions(+), 30 deletions(-) diff --git a/src/main/kotlin/app/termora/EditHostOptionsPane.kt b/src/main/kotlin/app/termora/EditHostOptionsPane.kt index 36cab47..b24917f 100644 --- a/src/main/kotlin/app/termora/EditHostOptionsPane.kt +++ b/src/main/kotlin/app/termora/EditHostOptionsPane.kt @@ -1,8 +1,5 @@ package app.termora -import app.termora.keymgr.KeyManager -import app.termora.keymgr.OhKeyPair - class EditHostOptionsPane(private val host: Host) : HostOptionsPane() { init { generalOption.portTextField.value = host.port @@ -10,15 +7,12 @@ class EditHostOptionsPane(private val host: Host) : HostOptionsPane() { generalOption.protocolTypeComboBox.selectedItem = host.protocol generalOption.usernameTextField.text = host.username generalOption.hostTextField.text = host.host - generalOption.passwordTextField.text = host.authentication.password generalOption.remarkTextArea.text = host.remark generalOption.authenticationTypeComboBox.selectedItem = host.authentication.type - if (host.authentication.type == AuthenticationType.PublicKey) { - val ohKeyPair = KeyManager.getInstance().getOhKeyPair(host.authentication.password) - if (ohKeyPair != null) { - generalOption.publicKeyTextField.text = ohKeyPair.name - generalOption.publicKeyTextField.putClientProperty(OhKeyPair::class, ohKeyPair) - } + if (host.authentication.type == AuthenticationType.Password) { + generalOption.passwordTextField.text = host.authentication.password + } else if (host.authentication.type == AuthenticationType.PublicKey) { + generalOption.publicKeyComboBox.selectedItem = host.authentication.password } proxyOption.proxyTypeComboBox.selectedItem = host.proxy.type diff --git a/src/main/kotlin/app/termora/HostOptionsPane.kt b/src/main/kotlin/app/termora/HostOptionsPane.kt index 94ab4b8..0361729 100644 --- a/src/main/kotlin/app/termora/HostOptionsPane.kt +++ b/src/main/kotlin/app/termora/HostOptionsPane.kt @@ -1,7 +1,7 @@ package app.termora +import app.termora.keymgr.KeyManager import app.termora.keymgr.KeyManagerDialog -import app.termora.keymgr.OhKeyPair import com.formdev.flatlaf.FlatClientProperties import com.formdev.flatlaf.extras.components.FlatComboBox import com.formdev.flatlaf.ui.FlatTextBorder @@ -49,10 +49,9 @@ open class HostOptionsPane : OptionsPane() { password = String(generalOption.passwordTextField.password) ) } else if (generalOption.authenticationTypeComboBox.selectedItem == AuthenticationType.PublicKey) { - val keyPair = generalOption.publicKeyTextField.getClientProperty(OhKeyPair::class) as OhKeyPair? authentication = authentication.copy( 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 } } else if (host.authentication.type == AuthenticationType.PublicKey) { - if (validateField(generalOption.publicKeyTextField)) { + if (validateField(generalOption.publicKeyComboBox)) { return false } } @@ -149,6 +148,19 @@ open class HostOptionsPane : OptionsPane() { 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 { val portTextField = PortSpinner() val nameTextField = OutlineTextField(128) @@ -158,7 +170,7 @@ open class HostOptionsPane : OptionsPane() { private val passwordPanel = JPanel(BorderLayout()) private val chooseKeyBtn = JButton(Icons.greyKey) val passwordTextField = OutlinePasswordField(255) - val publicKeyTextField = OutlineTextField() + val publicKeyComboBox = OutlineComboBox() val remarkTextArea = FixedLengthTextArea(512) val authenticationTypeComboBox = FlatComboBox() @@ -170,7 +182,7 @@ open class HostOptionsPane : OptionsPane() { private fun initView() { add(getCenterComponent(), BorderLayout.CENTER) - publicKeyTextField.isEditable = false + publicKeyComboBox.isEditable = false chooseKeyBtn.isFocusable = false 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() { override fun getListCellRendererComponent( list: JList<*>?, @@ -269,14 +303,20 @@ open class HostOptionsPane : OptionsPane() { dialog.pack() dialog.setLocationRelativeTo(null) dialog.isVisible = true - if (dialog.ok) { - val lastKeyPair = dialog.getLasOhKeyPair() - if (lastKeyPair != null) { - publicKeyTextField.putClientProperty(OhKeyPair::class, lastKeyPair) - publicKeyTextField.text = lastKeyPair.name - publicKeyTextField.outline = null - } + + val selectedItem = publicKeyComboBox.selectedItem + + publicKeyComboBox.removeAllItems() + for (keyPair in KeyManager.getInstance().getOhKeyPairs()) { + publicKeyComboBox.addItem(keyPair.id) } + publicKeyComboBox.selectedItem = selectedItem + + if (!dialog.ok) { + return + } + + publicKeyComboBox.selectedItem = dialog.getLasOhKeyPair()?.id ?: return } private fun refreshStates() { @@ -284,6 +324,7 @@ open class HostOptionsPane : OptionsPane() { portTextField.isEnabled = true usernameTextField.isEnabled = true authenticationTypeComboBox.isEnabled = true + publicKeyComboBox.isEnabled = true passwordTextField.isEnabled = true chooseKeyBtn.isEnabled = true @@ -293,6 +334,7 @@ open class HostOptionsPane : OptionsPane() { usernameTextField.isEnabled = false authenticationTypeComboBox.isEnabled = false passwordTextField.isEnabled = false + publicKeyComboBox.isEnabled = false chooseKeyBtn.isEnabled = false } @@ -369,10 +411,16 @@ open class HostOptionsPane : OptionsPane() { passwordPanel.removeAll() 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( FormBuilder.create() .layout(FormLayout("default:grow, 4dlu, left:pref", "pref")) - .add(publicKeyTextField).xy(1, 1) + .add(publicKeyComboBox).xy(1, 1) .add(chooseKeyBtn).xy(3, 1) .build(), BorderLayout.CENTER ) diff --git a/src/main/kotlin/app/termora/HostTree.kt b/src/main/kotlin/app/termora/HostTree.kt index af8db55..2ba6bb3 100644 --- a/src/main/kotlin/app/termora/HostTree.kt +++ b/src/main/kotlin/app/termora/HostTree.kt @@ -522,13 +522,18 @@ class HostTree : JTree(), Disposable { while (parents.isNotEmpty()) { val p = parents.removeFirst() - for (i in 0 until model.getChildCount(p)) { - val child = model.getChild(p, i) as Host + for (i in 0 until getModel().getChildCount(p)) { + val child = getModel().getChild(p, i) as Host nodes.add(child) parents.add(child) } } + // 确保是最新的 + for (i in 0 until nodes.size) { + nodes[i] = model.getHost(nodes[i].id) ?: continue + } + return nodes } diff --git a/src/main/kotlin/app/termora/TextField.kt b/src/main/kotlin/app/termora/TextField.kt index 1d6154a..d789564 100644 --- a/src/main/kotlin/app/termora/TextField.kt +++ b/src/main/kotlin/app/termora/TextField.kt @@ -1,13 +1,11 @@ package app.termora +import com.formdev.flatlaf.FlatClientProperties import com.formdev.flatlaf.extras.components.* import com.formdev.flatlaf.ui.FlatTextBorder import org.apache.commons.lang3.StringUtils import java.awt.Component -import java.awt.event.FocusAdapter -import java.awt.event.FocusEvent -import java.awt.event.KeyAdapter -import java.awt.event.KeyEvent +import java.awt.event.* import java.text.ParseException import javax.swing.DefaultListCellRenderer import javax.swing.JComboBox @@ -53,6 +51,15 @@ class OutlineTextArea : FlatTextArea() { } } +class OutlineComboBox : JComboBox() { + init { + addItemListener { + if (it.stateChange == ItemEvent.SELECTED) { + putClientProperty(FlatClientProperties.OUTLINE, null) + } + } + } +} class FixedLengthTextArea(var maxLength: Int = Int.MAX_VALUE) : FlatTextArea() { init {