From 2fc381caa54b3713f367fccbbbf5d60604d88c50 Mon Sep 17 00:00:00 2001 From: hstyi Date: Mon, 8 Sep 2025 11:00:17 +0800 Subject: [PATCH] fix: virtual window auto hiding --- .../terminal/panel/vw/TransferVisualWindow.kt | 44 ++++++++++++++++++- .../terminal/panel/vw/VisualWindowPanel.kt | 12 +++-- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/app/termora/terminal/panel/vw/TransferVisualWindow.kt b/src/main/kotlin/app/termora/terminal/panel/vw/TransferVisualWindow.kt index 21e9583..ec36aa4 100644 --- a/src/main/kotlin/app/termora/terminal/panel/vw/TransferVisualWindow.kt +++ b/src/main/kotlin/app/termora/terminal/panel/vw/TransferVisualWindow.kt @@ -30,6 +30,8 @@ import java.nio.file.Path import java.util.concurrent.CompletableFuture import java.util.concurrent.Executors import javax.swing.* +import javax.swing.event.PopupMenuEvent +import javax.swing.event.PopupMenuListener import kotlin.io.path.absolutePathString import kotlin.math.max import kotlin.reflect.cast @@ -58,12 +60,24 @@ internal class TransferVisualWindow(tab: SSHTerminalTab, visualWindowManager: Vi private val connectFailedPanel = ConnectFailedPanel() private val transferManager = TransferTableModel(coroutineScope) private val disposable = Disposer.newDisposable() + private val focusedWindow get() = KeyboardFocusManager.getCurrentKeyboardFocusManager().focusedWindow private val owner get() = SwingUtilities.getWindowAncestor(this) private val questionBtn = JButton(Icons.questionMark) private val downloadBtn = JButton(Icons.download) private val badgePresentation = Badge.getInstance(tab.windowScope) .addBadge(downloadBtn).apply { visible = false } private val support = DataProviderSupport() + private var isShowPopupMenu = false + + override var isStickHover: Boolean + get() = super.isStickHover + set(value) { + if (isShowPopupMenu || owner != focusedWindow) { + super.isStickHover = true + } else { + super.isStickHover = value + } + } init { initViews() @@ -135,6 +149,8 @@ internal class TransferVisualWindow(tab: SSHTerminalTab, visualWindowManager: Vi } }) + questionBtn.toolTipText = I18n.getString("termora.visual-window.transport.question") + // 立即连接 connect() } @@ -151,7 +167,7 @@ internal class TransferVisualWindow(tab: SSHTerminalTab, visualWindowManager: Vi val support = DefaultTransportSupport(fileSystem, fileSystem.defaultDir) withContext(Dispatchers.Swing) { val internalTransferManager = MyInternalTransferManager() - val transportPanel = TransportPanel( + val transportPanel = object : TransportPanel( internalTransferManager, tab.host, object : TransportSupportLoader { override suspend fun getTransportSupport(): TransportSupport { @@ -165,7 +181,27 @@ internal class TransferVisualWindow(tab: SSHTerminalTab, visualWindowManager: Vi override fun isLoaded(): Boolean { return true } - }) + }) { + override fun customizeContextmenu( + rows: Array, + e: MouseEvent, + popupMenu: TransportPopupMenu + ) { + popupMenu.addPopupMenuListener(object : PopupMenuListener { + override fun popupMenuWillBecomeVisible(e: PopupMenuEvent?) { + isShowPopupMenu = true + } + + override fun popupMenuWillBecomeInvisible(e: PopupMenuEvent?) { + isShowPopupMenu = false + } + + override fun popupMenuCanceled(e: PopupMenuEvent?) { + isShowPopupMenu = false + } + }) + } + } internalTransferManager.setTransferPanel(transportPanel) Disposer.register(transportPanel, object : Disposable { override fun dispose() { @@ -240,6 +276,10 @@ internal class TransferVisualWindow(tab: SSHTerminalTab, visualWindowManager: Vi super.dispose() } + override fun reassemble() { + super.reassemble() + } + override fun getData(dataKey: DataKey): T? { return support.getData(dataKey) } diff --git a/src/main/kotlin/app/termora/terminal/panel/vw/VisualWindowPanel.kt b/src/main/kotlin/app/termora/terminal/panel/vw/VisualWindowPanel.kt index b0f0dd7..9fadd37 100644 --- a/src/main/kotlin/app/termora/terminal/panel/vw/VisualWindowPanel.kt +++ b/src/main/kotlin/app/termora/terminal/panel/vw/VisualWindowPanel.kt @@ -36,6 +36,7 @@ open class VisualWindowPanel(protected val id: String, protected val visualWindo private var dialog: VisualWindowDialog? = null private var oldBounds = Rectangle() private var toggleWindowBtn = JButton(Icons.openInNewWindow) + private val closeBtn = JButton(Icons.close) private var isAlwaysTop get() = properties.getString("VisualWindow.${id}.dialog.isAlwaysTop", "false").toBoolean() set(value) = properties.putString("VisualWindow.${id}.dialog.isAlwaysTop", value.toString()) @@ -47,8 +48,8 @@ open class VisualWindowPanel(protected val id: String, protected val visualWindo } } - protected var isStickHover = false - private set(value) { + protected open var isStickHover = false + set(value) { if (value == field) return field = value reassemble() @@ -92,6 +93,8 @@ open class VisualWindowPanel(protected val id: String, protected val visualWindo oldBounds = bounds alwaysTopBtn.isSelected = isAlwaysTop alwaysTopBtn.isVisible = false + + closeBtn.toolTipText = I18n.getString("termora.tabbed.contextmenu.close") } protected open fun toolbarButtons(): List> { @@ -134,6 +137,7 @@ open class VisualWindowPanel(protected val id: String, protected val visualWindo addMouseListener(object : MouseAdapter() {}) toggleWindowBtn.addActionListener { toggleWindow() } + toggleWindowBtn.toolTipText = I18n.getString("termora.visual-window.toggle-window") addPropertyChangeListener("isWindow") { if (isWindow) { @@ -165,6 +169,8 @@ open class VisualWindowPanel(protected val id: String, protected val visualWindo dialog?.isAlwaysOnTop = isAlwaysTop } } + + closeBtn.addActionListener { if (beforeClose()) Disposer.dispose(visualWindow) } } private fun initToolBar() { @@ -180,7 +186,7 @@ open class VisualWindowPanel(protected val id: String, protected val visualWindo buttons.filter { it.second == Position.Right }.forEach { toolbar.add(it.first) } toolbar.add(toggleWindowBtn) - toolbar.add(JButton(Icons.close).apply { addActionListener { if (beforeClose()) Disposer.dispose(visualWindow) } }) + toolbar.add(closeBtn) toolbar.border = BorderFactory.createMatteBorder(0, 0, 1, 0, DynamicColor.BorderColor) add(toolbar, BorderLayout.NORTH) }