fix: abnormal operation of transfer dialog

This commit is contained in:
hstyi
2025-06-22 14:32:08 +08:00
committed by hstyi
parent 187d5be658
commit 7532546c77
2 changed files with 97 additions and 16 deletions

View File

@@ -222,7 +222,7 @@ class ApplicationRunner {
UIManager.put("TabbedPane.tabHeight", UIManager.getInt("TitleBar.height") - 6) UIManager.put("TabbedPane.tabHeight", UIManager.getInt("TitleBar.height") - 6)
} }
if (SystemInfo.isLinux) { if (SystemInfo.isLinux || SystemInfo.isWindows) {
UIManager.put("TitlePane.centerTitle", true) UIManager.put("TitlePane.centerTitle", true)
UIManager.put("TitlePane.showIcon", false) UIManager.put("TitlePane.showIcon", false)
UIManager.put("TitlePane.showIconInDialogs", false) UIManager.put("TitlePane.showIconInDialogs", false)

View File

@@ -9,7 +9,9 @@ import app.termora.plugin.internal.ssh.SSHTerminalTab.Companion.SSHSession
import app.termora.terminal.DataKey import app.termora.terminal.DataKey
import app.termora.terminal.DataListener import app.termora.terminal.DataListener
import app.termora.transfer.* import app.termora.transfer.*
import com.formdev.flatlaf.FlatClientProperties
import com.formdev.flatlaf.icons.FlatOptionPaneErrorIcon import com.formdev.flatlaf.icons.FlatOptionPaneErrorIcon
import com.formdev.flatlaf.util.SystemInfo
import com.jgoodies.forms.builder.FormBuilder import com.jgoodies.forms.builder.FormBuilder
import com.jgoodies.forms.layout.FormLayout import com.jgoodies.forms.layout.FormLayout
import kotlinx.coroutines.* import kotlinx.coroutines.*
@@ -20,17 +22,17 @@ import org.apache.sshd.client.session.ClientSession
import org.apache.sshd.sftp.client.SftpClientFactory import org.apache.sshd.sftp.client.SftpClientFactory
import org.jdesktop.swingx.JXBusyLabel import org.jdesktop.swingx.JXBusyLabel
import org.jdesktop.swingx.JXHyperlink import org.jdesktop.swingx.JXHyperlink
import org.jdesktop.swingx.JXTree
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.awt.* import java.awt.*
import java.awt.event.ActionEvent import java.awt.event.*
import java.awt.event.KeyEvent import java.net.URI
import java.awt.event.WindowAdapter
import java.awt.event.WindowEvent
import java.nio.file.Path import java.nio.file.Path
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
import java.util.concurrent.Executors import java.util.concurrent.Executors
import javax.swing.* import javax.swing.*
import kotlin.io.path.absolutePathString import kotlin.io.path.absolutePathString
import kotlin.math.max
import kotlin.reflect.cast import kotlin.reflect.cast
import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.milliseconds
@@ -83,6 +85,7 @@ class TransferVisualWindow(tab: SSHTerminalTab, visualWindowManager: VisualWindo
private fun initEvents() { private fun initEvents() {
Disposer.register(tab, this) Disposer.register(tab, this)
Disposer.register(this, disposable) Disposer.register(this, disposable)
Disposer.register(disposable, transferManager)
connectingPanel.busyLabel.isBusy = true connectingPanel.busyLabel.isBusy = true
@@ -103,8 +106,9 @@ class TransferVisualWindow(tab: SSHTerminalTab, visualWindowManager: VisualWindo
downloadBtn.addActionListener(object : AbstractAction() { downloadBtn.addActionListener(object : AbstractAction() {
override fun actionPerformed(e: ActionEvent) { override fun actionPerformed(e: ActionEvent) {
val dialog = DownloadDialog() val dialog = DownloadDialog()
dialog.iconImages = owner.iconImages
dialog.setLocationRelativeTo(downloadBtn) dialog.setLocationRelativeTo(downloadBtn)
dialog.setLocation(dialog.x, downloadBtn.locationOnScreen.y + downloadBtn.height + 1) dialog.setLocation(dialog.x, downloadBtn.locationOnScreen.y + downloadBtn.height + 2)
dialog.isVisible = true dialog.isVisible = true
} }
}) })
@@ -120,6 +124,12 @@ class TransferVisualWindow(tab: SSHTerminalTab, visualWindowManager: VisualWindo
} }
}) })
questionBtn.addActionListener(object : AbstractAction() {
override fun actionPerformed(e: ActionEvent) {
Application.browse(URI.create("https://github.com/TermoraDev/termora/pull/690"))
}
})
// 立即连接 // 立即连接
connect() connect()
} }
@@ -219,30 +229,86 @@ class TransferVisualWindow(tab: SSHTerminalTab, visualWindowManager: VisualWindo
} }
private inner class DownloadDialog() : JDialog() { private inner class DownloadDialog() : JDialog() {
init { init {
size = Dimension(UIManager.getInt("Dialog.width") - 150, UIManager.getInt("Dialog.height") - 100) size = getMySize()
isModal = false isModal = false
title = I18n.getString("termora.transport.sftp") title = I18n.getString("termora.transport.sftp")
isUndecorated = true
layout = BorderLayout() layout = BorderLayout()
if (SystemInfo.isMacOS) {
rootPane.putClientProperty("apple.awt.windowTitleVisible", false)
rootPane.putClientProperty("apple.awt.fullWindowContent", true)
rootPane.putClientProperty("apple.awt.transparentTitleBar", true)
}
add(createCenterPanel(), BorderLayout.CENTER) add(createCenterPanel(), BorderLayout.CENTER)
val window = this val window = this
addWindowFocusListener(object : WindowAdapter() {
override fun windowLostFocus(e: WindowEvent?) {
window.dispose()
}
})
val inputMap = rootPane.getInputMap(WHEN_IN_FOCUSED_WINDOW) val inputMap = rootPane.getInputMap(WHEN_IN_FOCUSED_WINDOW)
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "close") inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "close")
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, toolkit.menuShortcutKeyMaskEx), "close") inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, toolkit.menuShortcutKeyMaskEx), "close")
rootPane.actionMap.put("close", object : AnAction() { rootPane.actionMap.put("close", object : AnAction() {
override fun actionPerformed(evt: AnActionEvent) { override fun actionPerformed(evt: AnActionEvent) {
if (hasPopupMenus()) return if (hasPopupMenus().not()) {
SwingUtilities.invokeLater { window.dispose() } window.dispose()
}
} }
}) })
// 判断失去焦点
val awtEventListener = object : AWTEventListener {
override fun eventDispatched(event: AWTEvent) {
if (event !is MouseEvent) return
if (event.id != MouseEvent.MOUSE_PRESSED) return
val ancestor = SwingUtilities.getWindowAncestor(event.component)
if (ancestor == window) return
if (ancestor is Window && getOwners(ancestor).contains(window)) return
// JTreeTable 比较特殊,要特别判断
if (isFocused && event.component is JXTree) return
window.dispose()
}
private fun getOwners(window: Window): List<Window> {
val owners = mutableListOf<Window>()
var owner: Window? = window.owner
while (owner != null) {
owners.add(owner)
owner = owner.owner
}
return owners
}
}
// 监听全局事件
toolkit.addAWTEventListener(
awtEventListener,
MouseEvent.MOUSE_EVENT_MASK
)
addWindowListener(object : WindowAdapter() {
override fun windowClosed(e: WindowEvent) {
removeWindowListener(this)
toolkit.removeAWTEventListener(awtEventListener)
properties.putString("VisualWindow.DownloadDialog.location.width", width.toString())
properties.putString("VisualWindow.DownloadDialog.location.height", height.toString())
}
})
}
private fun getMySize(): Dimension {
val size = Dimension(UIManager.getInt("Dialog.width") - 150, UIManager.getInt("Dialog.height") - 100)
val width = properties.getString(
"VisualWindow.DownloadDialog.location.width",
size.width.toString()
).toIntOrNull() ?: size.width
val height = properties.getString(
"VisualWindow.DownloadDialog.location.height",
size.height.toString()
).toIntOrNull() ?: size.height
return Dimension(max(width, 250), max(height, 150))
} }
private fun hasPopupMenus(): Boolean { private fun hasPopupMenus(): Boolean {
@@ -290,6 +356,21 @@ class TransferVisualWindow(tab: SSHTerminalTab, visualWindowManager: VisualWindo
return scrollPane return scrollPane
} }
override fun addNotify() {
super.addNotify()
if (SystemInfo.isMacOS) {
NativeMacLibrary.setControlsVisible(this, false)
} else if (SystemInfo.isWindows || SystemInfo.isLinux) {
rootPane.putClientProperty(FlatClientProperties.FULL_WINDOW_CONTENT, true)
rootPane.putClientProperty(FlatClientProperties.TITLE_BAR_SHOW_ICONIFFY, false)
rootPane.putClientProperty(FlatClientProperties.TITLE_BAR_SHOW_ICON, false)
rootPane.putClientProperty(FlatClientProperties.TITLE_BAR_SHOW_MAXIMIZE, false)
rootPane.putClientProperty(FlatClientProperties.TITLE_BAR_SHOW_TITLE, false)
rootPane.putClientProperty(FlatClientProperties.TITLE_BAR_SHOW_CLOSE, false)
}
}
} }
private inner class MyInternalTransferManager() : InternalTransferManager { private inner class MyInternalTransferManager() : InternalTransferManager {