feat: use extension for floating toolbar

This commit is contained in:
hstyi
2025-07-03 11:40:21 +08:00
committed by hstyi
parent c0ecc9fa7d
commit 9ce4a88041
16 changed files with 300 additions and 131 deletions

View File

@@ -37,7 +37,7 @@ record ExtensionProxy(Plugin plugin, Extension extension) implements InvocationH
} }
throw new IllegalCallerException(target.getMessage(), target); throw new IllegalCallerException(target.getMessage(), target);
} }
throw e; throw target;
} }
} }
} }

View File

@@ -23,7 +23,10 @@ abstract class PtyHostTerminalTab(
private var readerJob: Job? = null private var readerJob: Job? = null
private val ptyConnectorDelegate = PtyConnectorDelegate() private val ptyConnectorDelegate = PtyConnectorDelegate()
protected val terminalPanel = TerminalPanelFactory.getInstance().createTerminalPanel(terminal, ptyConnectorDelegate) protected val terminalPanel = TerminalPanelFactory.getInstance().createTerminalPanel(
this,
terminal, ptyConnectorDelegate
)
protected val ptyConnectorFactory get() = PtyConnectorFactory.getInstance() protected val ptyConnectorFactory get() = PtyConnectorFactory.getInstance()
override fun start() { override fun start() {

View File

@@ -38,9 +38,9 @@ class TerminalPanelFactory : Disposable {
} }
fun createTerminalPanel(terminal: Terminal, ptyConnector: PtyConnector): TerminalPanel { fun createTerminalPanel(tab: TerminalTab?, terminal: Terminal, ptyConnector: PtyConnector): TerminalPanel {
val writer = MyTerminalWriter(ptyConnector) val writer = MyTerminalWriter(ptyConnector)
val terminalPanel = TerminalPanel(terminal, writer) val terminalPanel = TerminalPanel(tab, terminal, writer)
// processDeviceStatusReport // processDeviceStatusReport
terminal.getTerminalModel().setData(DataKey.TerminalWriter, writer) terminal.getTerminalModel().setData(DataKey.TerminalWriter, writer)

View File

@@ -82,8 +82,8 @@ class TermoraFrame : JFrame(), DataProvider {
if (scope != windowScope) return emptyList() if (scope != windowScope) return emptyList()
var filter = hostTreeModel.root.getAllChildren() var filter = hostTreeModel.root.getAllChildren()
.map { it.host }
.filter { it.isFolder.not() } .filter { it.isFolder.not() }
.map { it.host }
if (pattern.isNotBlank()) { if (pattern.isNotBlank()) {
filter = filter.filter { filter = filter.filter {

View File

@@ -1,5 +1,6 @@
package app.termora.actions package app.termora.actions
import org.apache.commons.lang3.StringUtils
import org.jdesktop.swingx.action.BoundAction import org.jdesktop.swingx.action.BoundAction
import java.awt.event.ActionEvent import java.awt.event.ActionEvent
import javax.swing.Icon import javax.swing.Icon
@@ -20,7 +21,7 @@ abstract class AnAction : BoundAction {
if (evt is AnActionEvent) { if (evt is AnActionEvent) {
actionPerformed(evt) actionPerformed(evt)
} else { } else {
actionPerformed(AnActionEvent(evt.source, evt.actionCommand, evt)) actionPerformed(AnActionEvent(evt.source, StringUtils.defaultString(evt.actionCommand), evt))
} }
} }

View File

@@ -40,7 +40,7 @@ class SSHCopyIdDialog(
} }
} }
private val terminalPanel by lazy { private val terminalPanel by lazy {
terminalPanelFactory.createTerminalPanel(terminal, PtyConnectorDelegate()) terminalPanelFactory.createTerminalPanel(null, terminal, PtyConnectorDelegate())
.apply { enableFloatingToolbar = false } .apply { enableFloatingToolbar = false }
} }
private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.IO) private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)

View File

@@ -13,6 +13,7 @@ import app.termora.plugin.internal.sftppty.SFTPPtyInternalPlugin
import app.termora.plugin.internal.ssh.SSHInternalPlugin import app.termora.plugin.internal.ssh.SSHInternalPlugin
import app.termora.plugin.internal.wsl.WSLInternalPlugin import app.termora.plugin.internal.wsl.WSLInternalPlugin
import app.termora.swingCoroutineScope import app.termora.swingCoroutineScope
import app.termora.terminal.panel.vw.FloatingToolbarPlugin
import app.termora.transfer.internal.local.LocalPlugin import app.termora.transfer.internal.local.LocalPlugin
import app.termora.transfer.internal.sftp.SFTPPlugin import app.termora.transfer.internal.sftp.SFTPPlugin
import com.formdev.flatlaf.util.SystemInfo import com.formdev.flatlaf.util.SystemInfo
@@ -127,6 +128,9 @@ internal class PluginManager private constructor() {
plugins.add(PluginDescriptor(LocalPlugin(), origin = PluginOrigin.Internal, version = version)) plugins.add(PluginDescriptor(LocalPlugin(), origin = PluginOrigin.Internal, version = version))
// sftp transfer plugin // sftp transfer plugin
plugins.add(PluginDescriptor(SFTPPlugin(), origin = PluginOrigin.Internal, version = version)) plugins.add(PluginDescriptor(SFTPPlugin(), origin = PluginOrigin.Internal, version = version))
// floating
plugins.add(PluginDescriptor(FloatingToolbarPlugin(), origin = PluginOrigin.Internal, version = version))
} }
private fun loadSystemPlugins() { private fun loadSystemPlugins() {

View File

@@ -0,0 +1,20 @@
package app.termora.terminal.panel
import app.termora.TerminalTab
import app.termora.actions.AnAction
import app.termora.plugin.Extension
import app.termora.terminal.panel.vw.VisualWindow
import app.termora.terminal.panel.vw.VisualWindowManager
interface FloatingToolbarActionExtension : Extension {
/**
* 抛出 [UnsupportedOperationException] 表示不支持
*/
fun createActionButton(visualWindowManager: VisualWindowManager, tab: TerminalTab): AnAction
/**
* 获取要返回的虚拟窗口
*/
fun getVisualWindowClass(tab: TerminalTab): Class<out VisualWindow>
}

View File

@@ -6,13 +6,10 @@ import app.termora.actions.AnActionEvent
import app.termora.actions.DataProvider import app.termora.actions.DataProvider
import app.termora.actions.DataProviders import app.termora.actions.DataProviders
import app.termora.database.DatabaseManager import app.termora.database.DatabaseManager
import app.termora.plugin.ExtensionManager
import app.termora.plugin.internal.ssh.SSHTerminalTab import app.termora.plugin.internal.ssh.SSHTerminalTab
import app.termora.snippet.SnippetAction
import app.termora.snippet.SnippetTreeDialog
import app.termora.terminal.DataKey import app.termora.terminal.DataKey
import app.termora.terminal.panel.vw.NvidiaSMIVisualWindow import app.termora.terminal.panel.vw.VisualWindowManager
import app.termora.terminal.panel.vw.SystemInformationVisualWindow
import app.termora.terminal.panel.vw.TransferVisualWindow
import com.formdev.flatlaf.extras.components.FlatToolBar import com.formdev.flatlaf.extras.components.FlatToolBar
import com.formdev.flatlaf.ui.FlatRoundBorder import com.formdev.flatlaf.ui.FlatRoundBorder
import org.apache.commons.lang3.StringUtils import org.apache.commons.lang3.StringUtils
@@ -26,7 +23,7 @@ import javax.swing.SwingUtilities
class FloatingToolbarPanel : FlatToolBar(), Disposable { class FloatingToolbarPanel : FlatToolBar(), Disposable {
private val floatingToolbarEnable get() = DatabaseManager.getInstance().terminal.floatingToolbar private val floatingToolbarEnable get() = DatabaseManager.getInstance().terminal.floatingToolbar
private var closed = false private var closed = false
private val anEvent get() = AnActionEvent(this, StringUtils.EMPTY, EventObject(this)) private val event get() = AnActionEvent(this, StringUtils.EMPTY, EventObject(this))
companion object { companion object {
@@ -79,7 +76,6 @@ class FloatingToolbarPanel : FlatToolBar(), Disposable {
} }
} }
initActions()
initEvents() initEvents()
} }
@@ -116,26 +112,36 @@ class FloatingToolbarPanel : FlatToolBar(), Disposable {
// Pin // Pin
add(initPinActionButton()) add(initPinActionButton())
// 服务器信息 val tab = event.getData(DataProviders.TerminalTab)
add(initServerInfoActionButton()) val terminalPanel = (tab as DataProvider?)?.getData(DataProviders.TerminalPanel)
if (terminalPanel != null) {
val extensions = ExtensionManager.getInstance()
.getExtensions(FloatingToolbarActionExtension::class.java)
for (extension in extensions) {
try {
add(createButton(extension.createActionButton(terminalPanel, tab), terminalPanel, tab, extension))
} catch (_: UnsupportedOperationException) {
continue
}
}
// Transfer initReconnectActionButton(tab)
add(initTransferActionButton()) }
// Snippet
add(initSnippetActionButton())
// Nvidia 显卡信息
add(initNvidiaSMIActionButton())
// 重连
add(initReconnectActionButton())
// 关闭 // 关闭
add(initCloseActionButton()) add(initCloseActionButton())
} }
private fun initEvents() { private fun initEvents() {
// 初始化 Action
addPropertyChangeListener("ancestor", object : PropertyChangeListener {
override fun propertyChange(evt: PropertyChangeEvent) {
removePropertyChangeListener("ancestor", this)
initActions()
}
})
// 被添加到组件后 // 被添加到组件后
addPropertyChangeListener("ancestor", object : PropertyChangeListener { addPropertyChangeListener("ancestor", object : PropertyChangeListener {
override fun propertyChange(evt: PropertyChangeEvent) { override fun propertyChange(evt: PropertyChangeEvent) {
@@ -143,11 +149,12 @@ class FloatingToolbarPanel : FlatToolBar(), Disposable {
SwingUtilities.invokeLater { resumeVisualWindows() } SwingUtilities.invokeLater { resumeVisualWindows() }
} }
}) })
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
private fun resumeVisualWindows() { private fun resumeVisualWindows() {
val tab = anEvent.getData(DataProviders.TerminalTab) ?: return val tab = event.getData(DataProviders.TerminalTab) ?: return
if (tab !is SSHTerminalTab) return if (tab !is SSHTerminalTab) return
val terminalPanel = tab.getData(DataProviders.TerminalPanel) ?: return val terminalPanel = tab.getData(DataProviders.TerminalPanel) ?: return
terminalPanel.resumeVisualWindows(tab.host.id, object : DataProvider { terminalPanel.resumeVisualWindows(tab.host.id, object : DataProvider {
@@ -160,106 +167,30 @@ class FloatingToolbarPanel : FlatToolBar(), Disposable {
}) })
} }
private fun createButton(
private fun initServerInfoActionButton(): JButton { action: AnAction,
val btn = JButton(Icons.infoOutline) visualWindowManager: VisualWindowManager,
btn.toolTipText = I18n.getString("termora.visual-window.system-information") tab: TerminalTab,
btn.addActionListener(object : AnAction() { extension: FloatingToolbarActionExtension
): JButton {
val btn = JButton(object : AnAction(action.smallIcon) {
override fun actionPerformed(evt: AnActionEvent) { override fun actionPerformed(evt: AnActionEvent) {
val tab = anEvent.getData(DataProviders.TerminalTab) ?: return try {
val terminalPanel = (tab as DataProvider?)?.getData(DataProviders.TerminalPanel) ?: return val clazz = extension.getVisualWindowClass(tab)
for (window in visualWindowManager.getVisualWindows()) {
if (tab !is SSHTerminalTab) { if (clazz.isInstance(window)) {
terminalPanel.toast(I18n.getString("termora.floating-toolbar.not-supported")) visualWindowManager.moveToFront(window)
return
}
for (window in terminalPanel.getVisualWindows()) {
if (window is SystemInformationVisualWindow) {
terminalPanel.moveToFront(window)
return return
} }
} }
action.actionPerformed(evt)
val visualWindowPanel = SystemInformationVisualWindow(tab, terminalPanel) } catch (_: UnsupportedOperationException) {
terminalPanel.addVisualWindow(visualWindowPanel) action.actionPerformed(evt)
}
}
})
return btn
}
private fun initTransferActionButton(): JButton {
val btn = JButton(Icons.folder)
btn.toolTipText = I18n.getString("termora.transport.sftp")
btn.addActionListener(object : AnAction() {
override fun actionPerformed(evt: AnActionEvent) {
val tab = anEvent.getData(DataProviders.TerminalTab) ?: return
val terminalPanel = (tab as DataProvider?)?.getData(DataProviders.TerminalPanel) ?: return
if (tab !is SSHTerminalTab) {
terminalPanel.toast(I18n.getString("termora.floating-toolbar.not-supported"))
return
}
for (window in terminalPanel.getVisualWindows()) {
if (window is TransferVisualWindow) {
terminalPanel.moveToFront(window)
return
}
}
val visualWindowPanel = TransferVisualWindow(tab, terminalPanel)
terminalPanel.addVisualWindow(visualWindowPanel)
}
})
return btn
}
private fun initSnippetActionButton(): JButton {
val btn = JButton(Icons.codeSpan)
btn.toolTipText = I18n.getString("termora.snippet.title")
btn.addActionListener(object : AnAction() {
override fun actionPerformed(evt: AnActionEvent) {
val tab = anEvent.getData(DataProviders.TerminalTab) ?: return
val writer = tab.getData(DataProviders.TerminalWriter) ?: return
val dialog = SnippetTreeDialog(evt.window)
dialog.setLocationRelativeTo(btn)
dialog.setLocation(dialog.x, btn.locationOnScreen.y + height + 2)
dialog.isVisible = true
val node = dialog.getSelectedNode() ?: return
SnippetAction.getInstance().runSnippet(node.data, writer)
}
})
return btn
}
private fun initNvidiaSMIActionButton(): JButton {
val btn = JButton(Icons.nvidia)
btn.toolTipText = I18n.getString("termora.visual-window.nvidia-smi")
btn.addActionListener(object : AnAction() {
override fun actionPerformed(evt: AnActionEvent) {
val tab = anEvent.getData(DataProviders.TerminalTab) ?: return
val terminalPanel = (tab as DataProvider?)?.getData(DataProviders.TerminalPanel) ?: return
if (tab !is SSHTerminalTab) {
terminalPanel.toast(I18n.getString("termora.floating-toolbar.not-supported"))
return
}
for (window in terminalPanel.getVisualWindows()) {
if (window is NvidiaSMIVisualWindow) {
terminalPanel.moveToFront(window)
return
}
}
val visualWindowPanel = NvidiaSMIVisualWindow(tab, terminalPanel)
terminalPanel.addVisualWindow(visualWindowPanel)
} }
}) })
btn.text = StringUtils.EMPTY
btn.toolTipText = action.shortDescription
return btn return btn
} }
@@ -293,19 +224,16 @@ class FloatingToolbarPanel : FlatToolBar(), Disposable {
return btn return btn
} }
private fun initReconnectActionButton(): JButton { private fun initReconnectActionButton(tab: TerminalTab) {
if (tab.canReconnect().not()) return
val btn = JButton(Icons.refresh) val btn = JButton(Icons.refresh)
btn.toolTipText = I18n.getString("termora.tabbed.contextmenu.reconnect") btn.toolTipText = I18n.getString("termora.tabbed.contextmenu.reconnect")
btn.addActionListener(object : AnAction() { btn.addActionListener(object : AnAction() {
override fun actionPerformed(evt: AnActionEvent) { override fun actionPerformed(evt: AnActionEvent) {
val tab = anEvent.getData(DataProviders.TerminalTab) ?: return
if (tab.canReconnect()) {
tab.reconnect() tab.reconnect()
} }
}
}) })
return btn add(btn)
} }
} }

View File

@@ -2,6 +2,7 @@ package app.termora.terminal.panel
import app.termora.Disposable import app.termora.Disposable
import app.termora.Disposer import app.termora.Disposer
import app.termora.TerminalTab
import app.termora.actions.DataProvider import app.termora.actions.DataProvider
import app.termora.actions.DataProviderSupport import app.termora.actions.DataProviderSupport
import app.termora.actions.DataProviders import app.termora.actions.DataProviders
@@ -35,7 +36,7 @@ import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.milliseconds
class TerminalPanel(val terminal: Terminal, private val writer: TerminalWriter) : class TerminalPanel(val tab: TerminalTab?, val terminal: Terminal, private val writer: TerminalWriter) :
JPanel(BorderLayout()), DataProvider, Disposable, VisualWindowManager { JPanel(BorderLayout()), DataProvider, Disposable, VisualWindowManager {
companion object { companion object {
@@ -554,7 +555,13 @@ class TerminalPanel(val terminal: Terminal, private val writer: TerminalWriter)
} }
} }
@Suppress("UNCHECKED_CAST")
override fun <T : Any> getData(dataKey: DataKey<T>): T? { override fun <T : Any> getData(dataKey: DataKey<T>): T? {
if (dataKey == DataProviders.TerminalTab) {
if (tab != null) {
return tab as T
}
}
return dataProviderSupport.getData(dataKey) return dataProviderSupport.getData(dataKey)
} }

View File

@@ -0,0 +1,27 @@
package app.termora.terminal.panel.vw
import app.termora.plugin.Extension
import app.termora.plugin.InternalPlugin
import app.termora.terminal.panel.FloatingToolbarActionExtension
import app.termora.terminal.panel.vw.extensions.NvidiaVisualWindowActionExtension
import app.termora.terminal.panel.vw.extensions.ServerInfoVisualWindowActionExtension
import app.termora.terminal.panel.vw.extensions.SnippetVisualWindowActionExtension
import app.termora.terminal.panel.vw.extensions.TransferVisualWindowActionExtension
internal class FloatingToolbarPlugin : InternalPlugin() {
init {
support.addExtension(FloatingToolbarActionExtension::class.java) { TransferVisualWindowActionExtension.instance }
support.addExtension(FloatingToolbarActionExtension::class.java) { ServerInfoVisualWindowActionExtension.instance }
support.addExtension(FloatingToolbarActionExtension::class.java) { SnippetVisualWindowActionExtension.instance }
support.addExtension(FloatingToolbarActionExtension::class.java) { NvidiaVisualWindowActionExtension.instance }
}
override fun getName(): String {
return "FloatingToolbar"
}
override fun <T : Extension> getExtensions(clazz: Class<T>): List<T> {
return support.getExtensions(clazz)
}
}

View File

@@ -10,6 +10,7 @@ import com.jgoodies.forms.builder.FormBuilder
import com.jgoodies.forms.layout.FormLayout import com.jgoodies.forms.layout.FormLayout
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.swing.Swing import kotlinx.coroutines.swing.Swing
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.apache.commons.lang3.StringUtils import org.apache.commons.lang3.StringUtils
@@ -25,6 +26,7 @@ import java.io.StringReader
import javax.swing.* import javax.swing.*
import javax.xml.parsers.DocumentBuilderFactory import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.xpath.XPathFactory import javax.xml.xpath.XPathFactory
import kotlin.time.Duration.Companion.milliseconds
class NvidiaSMIVisualWindow(tab: SSHTerminalTab, visualWindowManager: VisualWindowManager) : class NvidiaSMIVisualWindow(tab: SSHTerminalTab, visualWindowManager: VisualWindowManager) :
SSHVisualWindow(tab, "NVIDIA-SMI", visualWindowManager) { SSHVisualWindow(tab, "NVIDIA-SMI", visualWindowManager) {
@@ -264,7 +266,14 @@ class NvidiaSMIVisualWindow(tab: SSHTerminalTab, visualWindowManager: VisualWind
override suspend fun refresh(isFirst: Boolean) { override suspend fun refresh(isFirst: Boolean) {
val session = tab.getData(SSHTerminalTab.SSHSession) ?: return val session = suspend {
var c = tab.getData(SSHTerminalTab.SSHSession)
while (c == null) {
delay(250.milliseconds)
c = tab.getData(SSHTerminalTab.SSHSession)
}
c
}.invoke()
val doc = try { val doc = try {
val (code, text) = SshClients.execChannel(session, "nvidia-smi -x -q") val (code, text) = SshClients.execChannel(session, "nvidia-smi -x -q")

View File

@@ -0,0 +1,41 @@
package app.termora.terminal.panel.vw.extensions
import app.termora.I18n
import app.termora.Icons
import app.termora.TerminalTab
import app.termora.actions.AnAction
import app.termora.actions.AnActionEvent
import app.termora.plugin.internal.ssh.SSHTerminalTab
import app.termora.terminal.panel.FloatingToolbarActionExtension
import app.termora.terminal.panel.vw.NvidiaSMIVisualWindow
import app.termora.terminal.panel.vw.VisualWindow
import app.termora.terminal.panel.vw.VisualWindowManager
class NvidiaVisualWindowActionExtension private constructor() : FloatingToolbarActionExtension {
companion object {
val instance = NvidiaVisualWindowActionExtension()
}
override fun createActionButton(visualWindowManager: VisualWindowManager, tab: TerminalTab): AnAction {
if (tab !is SSHTerminalTab) throw UnsupportedOperationException()
return object : AnAction(Icons.nvidia) {
init {
putValue(SHORT_DESCRIPTION, I18n.getString("termora.visual-window.nvidia-smi"))
}
override fun actionPerformed(evt: AnActionEvent) {
val visualWindowPanel = NvidiaSMIVisualWindow(tab, visualWindowManager)
visualWindowManager.addVisualWindow(visualWindowPanel)
}
}
}
override fun getVisualWindowClass(tab: TerminalTab): Class<out VisualWindow> {
return NvidiaSMIVisualWindow::class.java
}
override fun ordered(): Long {
return 3;
}
}

View File

@@ -0,0 +1,42 @@
package app.termora.terminal.panel.vw.extensions
import app.termora.I18n
import app.termora.Icons
import app.termora.TerminalTab
import app.termora.actions.AnAction
import app.termora.actions.AnActionEvent
import app.termora.plugin.internal.ssh.SSHTerminalTab
import app.termora.terminal.panel.FloatingToolbarActionExtension
import app.termora.terminal.panel.vw.SystemInformationVisualWindow
import app.termora.terminal.panel.vw.VisualWindow
import app.termora.terminal.panel.vw.VisualWindowManager
class ServerInfoVisualWindowActionExtension private constructor() : FloatingToolbarActionExtension {
companion object {
val instance = ServerInfoVisualWindowActionExtension()
}
override fun createActionButton(visualWindowManager: VisualWindowManager, tab: TerminalTab): AnAction {
if (tab !is SSHTerminalTab) throw UnsupportedOperationException()
return object : AnAction(Icons.infoOutline) {
init {
putValue(SHORT_DESCRIPTION, I18n.getString("termora.visual-window.system-information"))
}
override fun actionPerformed(evt: AnActionEvent) {
val visualWindowPanel = SystemInformationVisualWindow(tab, visualWindowManager)
visualWindowManager.addVisualWindow(visualWindowPanel)
}
}
}
override fun getVisualWindowClass(tab: TerminalTab): Class<out VisualWindow> {
if (tab !is SSHTerminalTab) throw UnsupportedOperationException()
return SystemInformationVisualWindow::class.java
}
override fun ordered(): Long {
return -1;
}
}

View File

@@ -0,0 +1,50 @@
package app.termora.terminal.panel.vw.extensions
import app.termora.I18n
import app.termora.Icons
import app.termora.PtyHostTerminalTab
import app.termora.TerminalTab
import app.termora.actions.AnAction
import app.termora.actions.AnActionEvent
import app.termora.actions.DataProviders
import app.termora.snippet.SnippetAction
import app.termora.snippet.SnippetTreeDialog
import app.termora.terminal.panel.FloatingToolbarActionExtension
import app.termora.terminal.panel.vw.VisualWindow
import app.termora.terminal.panel.vw.VisualWindowManager
import javax.swing.JComponent
class SnippetVisualWindowActionExtension private constructor() : FloatingToolbarActionExtension {
companion object {
val instance = SnippetVisualWindowActionExtension()
}
override fun createActionButton(visualWindowManager: VisualWindowManager, tab: TerminalTab): AnAction {
if (tab !is PtyHostTerminalTab) throw UnsupportedOperationException()
return object : AnAction(Icons.codeSpan) {
init {
putValue(SHORT_DESCRIPTION, I18n.getString("termora.snippet.title"))
}
override fun actionPerformed(evt: AnActionEvent) {
val btn = evt.source as? JComponent ?: return
val writer = tab.getData(DataProviders.TerminalWriter) ?: return
val dialog = SnippetTreeDialog(evt.window)
dialog.setLocationRelativeTo(btn)
dialog.setLocation(dialog.x, btn.locationOnScreen.y + btn.height + 2)
dialog.isVisible = true
val node = dialog.getSelectedNode() ?: return
SnippetAction.getInstance().runSnippet(node.data, writer)
}
}
}
override fun getVisualWindowClass(tab: TerminalTab): Class<out VisualWindow> {
throw UnsupportedOperationException()
}
override fun ordered(): Long {
return 2;
}
}

View File

@@ -0,0 +1,37 @@
package app.termora.terminal.panel.vw.extensions
import app.termora.Icons
import app.termora.TerminalTab
import app.termora.actions.AnAction
import app.termora.actions.AnActionEvent
import app.termora.plugin.internal.ssh.SSHTerminalTab
import app.termora.terminal.panel.FloatingToolbarActionExtension
import app.termora.terminal.panel.vw.TransferVisualWindow
import app.termora.terminal.panel.vw.VisualWindow
import app.termora.terminal.panel.vw.VisualWindowManager
class TransferVisualWindowActionExtension private constructor() : FloatingToolbarActionExtension {
companion object {
val instance = TransferVisualWindowActionExtension()
}
override fun createActionButton(visualWindowManager: VisualWindowManager, tab: TerminalTab): AnAction {
if (tab !is SSHTerminalTab) throw UnsupportedOperationException()
return object : AnAction(Icons.folder) {
override fun actionPerformed(evt: AnActionEvent) {
val visualWindowPanel = TransferVisualWindow(tab, visualWindowManager)
visualWindowManager.addVisualWindow(visualWindowPanel)
}
}
}
override fun getVisualWindowClass(tab: TerminalTab): Class<out VisualWindow> {
if (tab !is SSHTerminalTab) throw UnsupportedOperationException()
return TransferVisualWindow::class.java
}
override fun ordered(): Long {
return 1
}
}