From c49acf7b51bb83c001d98e6fdd50feab08fab55b Mon Sep 17 00:00:00 2001 From: hstyi Date: Fri, 21 Feb 2025 17:04:50 +0800 Subject: [PATCH] feat: support fixed SFTP tab (#286) --- src/main/kotlin/app/termora/Database.kt | 6 +++ .../kotlin/app/termora/SFTPTerminalTab.kt | 14 +++++-- .../kotlin/app/termora/SettingsOptionsPane.kt | 41 ++++++++++++++++--- src/main/kotlin/app/termora/TerminalTabbed.kt | 23 +++++++---- .../app/termora/TerminalTabbedManager.kt | 4 +- src/main/kotlin/app/termora/TermoraFrame.kt | 12 ++++-- .../app/termora/transport/SFTPAction.kt | 8 ++-- src/main/resources/i18n/messages.properties | 1 + .../resources/i18n/messages_zh_CN.properties | 1 + .../resources/i18n/messages_zh_TW.properties | 1 + 10 files changed, 84 insertions(+), 27 deletions(-) diff --git a/src/main/kotlin/app/termora/Database.kt b/src/main/kotlin/app/termora/Database.kt index a156a47..ebbe281 100644 --- a/src/main/kotlin/app/termora/Database.kt +++ b/src/main/kotlin/app/termora/Database.kt @@ -599,6 +599,12 @@ class Database private constructor(private val env: Environment) : Disposable { */ var sftpCommand by StringPropertyDelegate(StringUtils.EMPTY) + + /** + * 是否固定在标签栏 + */ + var pinTab by BooleanPropertyDelegate(false) + } /** diff --git a/src/main/kotlin/app/termora/SFTPTerminalTab.kt b/src/main/kotlin/app/termora/SFTPTerminalTab.kt index f56629c..65b24d2 100644 --- a/src/main/kotlin/app/termora/SFTPTerminalTab.kt +++ b/src/main/kotlin/app/termora/SFTPTerminalTab.kt @@ -12,10 +12,11 @@ import javax.swing.SwingUtilities class SFTPTerminalTab : Disposable, TerminalTab, DataProvider { - private val transportPanel by lazy { - TransportPanel().apply { - Disposer.register(this@SFTPTerminalTab, this) - } + private val sftp get() = Database.getDatabase().sftp + private val transportPanel = TransportPanel() + + init { + Disposer.register(this, transportPanel) } override fun getTitle(): String { @@ -43,6 +44,11 @@ class SFTPTerminalTab : Disposable, TerminalTab, DataProvider { override fun canClose(): Boolean { assertEventDispatchThread() + + if (sftp.pinTab) { + return false + } + val transportManager = transportPanel.getData(TransportDataProviders.TransportManager) ?: return true if (transportManager.getTransports().isEmpty()) { return true diff --git a/src/main/kotlin/app/termora/SettingsOptionsPane.kt b/src/main/kotlin/app/termora/SettingsOptionsPane.kt index 5ecd48d..a0dc4f6 100644 --- a/src/main/kotlin/app/termora/SettingsOptionsPane.kt +++ b/src/main/kotlin/app/termora/SettingsOptionsPane.kt @@ -4,6 +4,7 @@ import app.termora.AES.encodeBase64String import app.termora.Application.ohMyJson import app.termora.actions.AnAction import app.termora.actions.AnActionEvent +import app.termora.actions.DataProviders import app.termora.highlight.KeywordHighlight import app.termora.highlight.KeywordHighlightManager import app.termora.keymap.Keymap @@ -22,6 +23,7 @@ import app.termora.terminal.CursorStyle import app.termora.terminal.DataKey import app.termora.terminal.panel.FloatingToolbarPanel import app.termora.terminal.panel.TerminalPanel +import app.termora.transport.SFTPAction import cash.z.ecc.android.bip39.Mnemonics import com.formdev.flatlaf.FlatClientProperties import com.formdev.flatlaf.extras.FlatSVGIcon @@ -43,6 +45,7 @@ import org.apache.commons.lang3.SystemUtils import org.apache.commons.lang3.exception.ExceptionUtils import org.apache.commons.lang3.time.DateFormatUtils import org.jdesktop.swingx.JXEditorPane +import org.jdesktop.swingx.action.ActionManager import org.slf4j.LoggerFactory import java.awt.BorderLayout import java.awt.Component @@ -67,6 +70,7 @@ class SettingsOptionsPane : OptionsPane() { private val hostManager get() = HostManager.getInstance() private val keymapManager get() = KeymapManager.getInstance() private val macroManager get() = MacroManager.getInstance() + private val actionManager get() = ActionManager.getInstance() private val keywordHighlightManager get() = KeywordHighlightManager.getInstance() private val keyManager get() = KeyManager.getInstance() @@ -1306,9 +1310,11 @@ class SettingsOptionsPane : OptionsPane() { private inner class SFTPOption : JPanel(BorderLayout()), Option { - val editCommandField = OutlineTextField(255) - val sftpCommandField = OutlineTextField(255) + private val editCommandField = OutlineTextField(255) + private val sftpCommandField = OutlineTextField(255) + private val pinTabComboBox = YesOrNoComboBox() private val sftp get() = database.sftp + private val sftpAction get() = actionManager.getAction(Actions.SFTP) as SFTPAction init { initView() @@ -1329,6 +1335,26 @@ class SettingsOptionsPane : OptionsPane() { sftp.sftpCommand = sftpCommandField.text } }) + + pinTabComboBox.addItemListener { + if (it.stateChange == ItemEvent.SELECTED) { + sftp.pinTab = pinTabComboBox.selectedItem as Boolean + for (window in TermoraFrameManager.getInstance().getWindows()) { + val evt = AnActionEvent(window, StringUtils.EMPTY, EventObject(window)) + if (pinTabComboBox.selectedItem == true) { + sftpAction.openOrCreateSFTPTerminalTab(evt) + } + val tabbed = evt.getData(DataProviders.TabbedPane) ?: continue + val manager = evt.getData(DataProviders.TerminalTabbedManager) ?: continue + for ((index, tab) in manager.getTerminalTabs().withIndex()) { + if (tab is SFTPTerminalTab) { + tabbed.setTabClosable(index, pinTabComboBox.selectedItem != true) + break + } + } + } + } + } } @@ -1347,6 +1373,7 @@ class SettingsOptionsPane : OptionsPane() { editCommandField.text = sftp.editCommand sftpCommandField.text = sftp.sftpCommand + pinTabComboBox.selectedItem = sftp.pinTab } override fun getIcon(isSelected: Boolean): Icon { @@ -1368,10 +1395,12 @@ class SettingsOptionsPane : OptionsPane() { ) val builder = FormBuilder.create().layout(layout).debug(false) - builder.add("${I18n.getString("termora.settings.sftp.edit-command")}:").xy(1, 1) - builder.add(editCommandField).xy(3, 1) - builder.add("${I18n.getString("termora.tabbed.contextmenu.sftp-command")}:").xy(1, 3) - builder.add(sftpCommandField).xy(3, 3) + builder.add("${I18n.getString("termora.settings.sftp.fixed-tab")}:").xy(1, 1) + builder.add(pinTabComboBox).xy(3, 1) + builder.add("${I18n.getString("termora.settings.sftp.edit-command")}:").xy(1, 3) + builder.add(editCommandField).xy(3, 3) + builder.add("${I18n.getString("termora.tabbed.contextmenu.sftp-command")}:").xy(1, 5) + builder.add(sftpCommandField).xy(3, 5) return builder.build() diff --git a/src/main/kotlin/app/termora/TerminalTabbed.kt b/src/main/kotlin/app/termora/TerminalTabbed.kt index 632ab98..84df7d4 100644 --- a/src/main/kotlin/app/termora/TerminalTabbed.kt +++ b/src/main/kotlin/app/termora/TerminalTabbed.kt @@ -280,7 +280,7 @@ class TerminalTabbed( } - close.isEnabled = c !is WelcomePanel + close.isEnabled = tab.canClose() rename.isEnabled = close.isEnabled clone.isEnabled = close.isEnabled openInNewWindow.isEnabled = close.isEnabled @@ -306,7 +306,7 @@ class TerminalTabbed( } - private fun addTab(index: Int, tab: TerminalTab) { + private fun addTab(index: Int, tab: TerminalTab, selected: Boolean) { val c = tab.getJComponent() val title = (c.getClientProperty(titleProperty) ?: tab.getTitle()).toString() @@ -317,13 +317,20 @@ class TerminalTabbed( StringUtils.EMPTY, index ) - c.putClientProperty(titleProperty, title) + // 设置标题 + c.putClientProperty(titleProperty, title) // 监听 icons 变化 tab.addPropertyChangeListener(iconListener) tabs.add(index, tab) - tabbedPane.selectedIndex = index + + if (selected) { + tabbedPane.selectedIndex = index + } + + tabbedPane.setTabClosable(index, tab.canClose()) + Disposer.register(this, tab) } @@ -445,12 +452,12 @@ class TerminalTabbed( override fun dispose() { } - override fun addTerminalTab(tab: TerminalTab) { - addTab(tabs.size, tab) + override fun addTerminalTab(tab: TerminalTab, selected: Boolean) { + addTab(tabs.size, tab, selected) } - override fun addTerminalTab(index: Int, tab: TerminalTab) { - addTab(index, tab) + override fun addTerminalTab(index: Int, tab: TerminalTab, selected: Boolean) { + addTab(index, tab, selected) } override fun getSelectedTerminalTab(): TerminalTab? { diff --git a/src/main/kotlin/app/termora/TerminalTabbedManager.kt b/src/main/kotlin/app/termora/TerminalTabbedManager.kt index f7b4c2e..07be15a 100644 --- a/src/main/kotlin/app/termora/TerminalTabbedManager.kt +++ b/src/main/kotlin/app/termora/TerminalTabbedManager.kt @@ -1,8 +1,8 @@ package app.termora interface TerminalTabbedManager { - fun addTerminalTab(tab: TerminalTab) - fun addTerminalTab(index: Int, tab: TerminalTab) + fun addTerminalTab(tab: TerminalTab, selected: Boolean = true) + fun addTerminalTab(index: Int, tab: TerminalTab, selected: Boolean = true) fun getSelectedTerminalTab(): TerminalTab? fun getTerminalTabs(): List fun setSelectedTerminalTab(tab: TerminalTab) diff --git a/src/main/kotlin/app/termora/TermoraFrame.kt b/src/main/kotlin/app/termora/TermoraFrame.kt index 28e628a..f99cd97 100644 --- a/src/main/kotlin/app/termora/TermoraFrame.kt +++ b/src/main/kotlin/app/termora/TermoraFrame.kt @@ -1,7 +1,6 @@ package app.termora -import app.termora.actions.ActionManager import app.termora.actions.DataProvider import app.termora.actions.DataProviderSupport import app.termora.actions.DataProviders @@ -12,7 +11,6 @@ import com.formdev.flatlaf.util.SystemInfo import com.jetbrains.JBR import java.awt.Dimension import java.awt.Insets -import java.awt.KeyboardFocusManager import java.awt.event.MouseAdapter import java.awt.event.MouseEvent import java.util.* @@ -32,7 +30,6 @@ fun assertEventDispatchThread() { class TermoraFrame : JFrame(), DataProvider { - private val actionManager get() = ActionManager.getInstance() private val id = UUID.randomUUID().toString() private val windowScope = ApplicationScope.forWindowScope(this) private val titleBar = LogicCustomTitleBar.createCustomTitleBar(this) @@ -42,7 +39,7 @@ class TermoraFrame : JFrame(), DataProvider { private val isWindowDecorationsSupported by lazy { JBR.isWindowDecorationsSupported() } private val dataProviderSupport = DataProviderSupport() private val welcomePanel = WelcomePanel(windowScope) - private val keyboardFocusManager by lazy { KeyboardFocusManager.getCurrentKeyboardFocusManager() } + private val sftp get() = Database.getDatabase().sftp init { @@ -103,6 +100,13 @@ class TermoraFrame : JFrame(), DataProvider { minimumSize = Dimension(640, 400) terminalTabbed.addTerminalTab(welcomePanel) + // 下一次事件循环检测是否固定 SFTP + SwingUtilities.invokeLater { + if (sftp.pinTab) { + terminalTabbed.addTerminalTab(SFTPTerminalTab(), false) + } + } + // macOS 要避开左边的控制栏 if (SystemInfo.isMacOS) { val left = max(titleBar.leftInset.toInt(), 76) diff --git a/src/main/kotlin/app/termora/transport/SFTPAction.kt b/src/main/kotlin/app/termora/transport/SFTPAction.kt index f4bf637..14d46e8 100644 --- a/src/main/kotlin/app/termora/transport/SFTPAction.kt +++ b/src/main/kotlin/app/termora/transport/SFTPAction.kt @@ -23,20 +23,22 @@ class SFTPAction : AnAction("SFTP", Icons.folder) { * * @return null 表示当前条件下无法创建 */ - fun openOrCreateSFTPTerminalTab(evt: AnActionEvent): SFTPTerminalTab? { + fun openOrCreateSFTPTerminalTab(evt: AnActionEvent, selected: Boolean = true): SFTPTerminalTab? { val terminalTabbedManager = evt.getData(DataProviders.TerminalTabbedManager) ?: return null val tabs = terminalTabbedManager.getTerminalTabs() for (tab in tabs) { if (tab is SFTPTerminalTab) { - terminalTabbedManager.setSelectedTerminalTab(tab) + if (selected) { + terminalTabbedManager.setSelectedTerminalTab(tab) + } return tab } } // 创建一个新的 val tab = SFTPTerminalTab() - terminalTabbedManager.addTerminalTab(tab) + terminalTabbedManager.addTerminalTab(tab, selected) return tab } diff --git a/src/main/resources/i18n/messages.properties b/src/main/resources/i18n/messages.properties index e69dce6..abe39e1 100644 --- a/src/main/resources/i18n/messages.properties +++ b/src/main/resources/i18n/messages.properties @@ -110,6 +110,7 @@ termora.settings.keymap.already-exists=The shortcut [{0}] is already in use by [ termora.settings.sftp.edit-command=Edit Command +termora.settings.sftp.fixed-tab=Fixed tab termora.settings.restart.title=Restart diff --git a/src/main/resources/i18n/messages_zh_CN.properties b/src/main/resources/i18n/messages_zh_CN.properties index e41d2dc..941da37 100644 --- a/src/main/resources/i18n/messages_zh_CN.properties +++ b/src/main/resources/i18n/messages_zh_CN.properties @@ -113,6 +113,7 @@ termora.settings.keymap.already-exists=快捷键 [{0}] 已经被 [{1}] 占用 termora.settings.sftp.edit-command=编辑命令 +termora.settings.sftp.fixed-tab=固定标签 # Welcome diff --git a/src/main/resources/i18n/messages_zh_TW.properties b/src/main/resources/i18n/messages_zh_TW.properties index fcfa305..65e3cda 100644 --- a/src/main/resources/i18n/messages_zh_TW.properties +++ b/src/main/resources/i18n/messages_zh_TW.properties @@ -63,6 +63,7 @@ termora.settings.keymap.action=操作 termora.settings.keymap.already-exists=快捷鍵 [{0}] 已經被 [{1}] 占用 termora.settings.sftp.edit-command=編輯命令 +termora.settings.sftp.fixed-tab=固定標籤 # Find everywhere