mirror of
https://github.com/TermoraDev/termora.git
synced 2026-01-16 02:12:58 +08:00
feat: support for restoring virtual windows
This commit is contained in:
@@ -52,6 +52,7 @@ class SSHTerminalTab(windowScope: WindowScope, host: Host) :
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
terminalPanel.dropFiles = false
|
terminalPanel.dropFiles = false
|
||||||
|
terminalPanel.dataProviderSupport.addData(DataProviders.TerminalTab, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getJComponent(): JComponent {
|
override fun getJComponent(): JComponent {
|
||||||
@@ -222,6 +223,11 @@ class SSHTerminalTab(windowScope: WindowScope, host: Host) :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun willBeClose(): Boolean {
|
||||||
|
// 保存窗口状态
|
||||||
|
terminalPanel.storeVisualWindows(host.id)
|
||||||
|
return super.willBeClose()
|
||||||
|
}
|
||||||
|
|
||||||
private inner class MySessionListener : SessionListener, Disposable {
|
private inner class MySessionListener : SessionListener, Disposable {
|
||||||
override fun sessionEvent(session: Session, event: Event) {
|
override fun sessionEvent(session: Session, event: Event) {
|
||||||
|
|||||||
@@ -14,11 +14,16 @@ 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
|
||||||
import java.awt.event.ActionListener
|
import java.awt.event.ActionListener
|
||||||
|
import java.beans.PropertyChangeEvent
|
||||||
|
import java.beans.PropertyChangeListener
|
||||||
|
import java.util.*
|
||||||
import javax.swing.JButton
|
import javax.swing.JButton
|
||||||
|
import javax.swing.SwingUtilities
|
||||||
|
|
||||||
class FloatingToolbarPanel : FlatToolBar(), Disposable {
|
class FloatingToolbarPanel : FlatToolBar(), Disposable {
|
||||||
private val floatingToolbarEnable get() = Database.getDatabase().terminal.floatingToolbar
|
private val floatingToolbarEnable get() = Database.getDatabase().terminal.floatingToolbar
|
||||||
private var closed = false
|
private var closed = false
|
||||||
|
private val anEvent get() = AnActionEvent(this, StringUtils.EMPTY, EventObject(this))
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
@@ -72,6 +77,7 @@ class FloatingToolbarPanel : FlatToolBar(), Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initActions()
|
initActions()
|
||||||
|
initEvents()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateUI() {
|
override fun updateUI() {
|
||||||
@@ -123,12 +129,38 @@ class FloatingToolbarPanel : FlatToolBar(), Disposable {
|
|||||||
add(initCloseActionButton())
|
add(initCloseActionButton())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun initEvents() {
|
||||||
|
// 被添加到组件后
|
||||||
|
addPropertyChangeListener("ancestor", object : PropertyChangeListener {
|
||||||
|
override fun propertyChange(evt: PropertyChangeEvent) {
|
||||||
|
removePropertyChangeListener("ancestor", this)
|
||||||
|
SwingUtilities.invokeLater { resumeVisualWindows() }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
private fun resumeVisualWindows() {
|
||||||
|
val tab = anEvent.getData(DataProviders.TerminalTab) ?: return
|
||||||
|
if (tab !is SSHTerminalTab) return
|
||||||
|
val terminalPanel = tab.getData(DataProviders.TerminalPanel) ?: return
|
||||||
|
terminalPanel.resumeVisualWindows(tab.host.id, object : DataProvider {
|
||||||
|
override fun <T : Any> getData(dataKey: DataKey<T>): T? {
|
||||||
|
if (dataKey == DataProviders.TerminalTab) {
|
||||||
|
return tab as T
|
||||||
|
}
|
||||||
|
return super.getData(dataKey)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun initServerInfoActionButton(): JButton {
|
private fun initServerInfoActionButton(): JButton {
|
||||||
val btn = JButton(Icons.infoOutline)
|
val btn = JButton(Icons.infoOutline)
|
||||||
btn.toolTipText = I18n.getString("termora.visual-window.system-information")
|
btn.toolTipText = I18n.getString("termora.visual-window.system-information")
|
||||||
btn.addActionListener(object : AnAction() {
|
btn.addActionListener(object : AnAction() {
|
||||||
override fun actionPerformed(evt: AnActionEvent) {
|
override fun actionPerformed(evt: AnActionEvent) {
|
||||||
val tab = evt.getData(DataProviders.TerminalTab) ?: return
|
val tab = anEvent.getData(DataProviders.TerminalTab) ?: return
|
||||||
val terminalPanel = (tab as DataProvider?)?.getData(DataProviders.TerminalPanel) ?: return
|
val terminalPanel = (tab as DataProvider?)?.getData(DataProviders.TerminalPanel) ?: return
|
||||||
|
|
||||||
if (tab !is SSHTerminalTab) {
|
if (tab !is SSHTerminalTab) {
|
||||||
@@ -156,7 +188,7 @@ class FloatingToolbarPanel : FlatToolBar(), Disposable {
|
|||||||
btn.toolTipText = I18n.getString("termora.snippet.title")
|
btn.toolTipText = I18n.getString("termora.snippet.title")
|
||||||
btn.addActionListener(object : AnAction() {
|
btn.addActionListener(object : AnAction() {
|
||||||
override fun actionPerformed(evt: AnActionEvent) {
|
override fun actionPerformed(evt: AnActionEvent) {
|
||||||
val tab = evt.getData(DataProviders.TerminalTab) ?: return
|
val tab = anEvent.getData(DataProviders.TerminalTab) ?: return
|
||||||
val writer = tab.getData(DataProviders.TerminalWriter) ?: return
|
val writer = tab.getData(DataProviders.TerminalWriter) ?: return
|
||||||
val dialog = SnippetTreeDialog(evt.window)
|
val dialog = SnippetTreeDialog(evt.window)
|
||||||
dialog.setLocationRelativeTo(btn)
|
dialog.setLocationRelativeTo(btn)
|
||||||
@@ -174,7 +206,7 @@ class FloatingToolbarPanel : FlatToolBar(), Disposable {
|
|||||||
btn.toolTipText = I18n.getString("termora.visual-window.nvidia-smi")
|
btn.toolTipText = I18n.getString("termora.visual-window.nvidia-smi")
|
||||||
btn.addActionListener(object : AnAction() {
|
btn.addActionListener(object : AnAction() {
|
||||||
override fun actionPerformed(evt: AnActionEvent) {
|
override fun actionPerformed(evt: AnActionEvent) {
|
||||||
val tab = evt.getData(DataProviders.TerminalTab) ?: return
|
val tab = anEvent.getData(DataProviders.TerminalTab) ?: return
|
||||||
val terminalPanel = (tab as DataProvider?)?.getData(DataProviders.TerminalPanel) ?: return
|
val terminalPanel = (tab as DataProvider?)?.getData(DataProviders.TerminalPanel) ?: return
|
||||||
|
|
||||||
if (tab !is SSHTerminalTab) {
|
if (tab !is SSHTerminalTab) {
|
||||||
@@ -233,7 +265,7 @@ class FloatingToolbarPanel : FlatToolBar(), Disposable {
|
|||||||
|
|
||||||
btn.addActionListener(object : AnAction() {
|
btn.addActionListener(object : AnAction() {
|
||||||
override fun actionPerformed(evt: AnActionEvent) {
|
override fun actionPerformed(evt: AnActionEvent) {
|
||||||
val tab = evt.getData(DataProviders.TerminalTab) ?: return
|
val tab = anEvent.getData(DataProviders.TerminalTab) ?: return
|
||||||
if (tab.canReconnect()) {
|
if (tab.canReconnect()) {
|
||||||
tab.reconnect()
|
tab.reconnect()
|
||||||
}
|
}
|
||||||
@@ -242,8 +274,4 @@ class FloatingToolbarPanel : FlatToolBar(), Disposable {
|
|||||||
return btn
|
return btn
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun dispose() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
package app.termora.terminal.panel
|
package app.termora.terminal.panel
|
||||||
|
|
||||||
|
import app.termora.Database
|
||||||
import app.termora.Disposable
|
import app.termora.Disposable
|
||||||
import app.termora.Disposer
|
import app.termora.Disposer
|
||||||
|
import app.termora.SSHTerminalTab
|
||||||
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
|
||||||
import app.termora.terminal.*
|
import app.termora.terminal.*
|
||||||
import app.termora.terminal.panel.vw.VisualWindow
|
import app.termora.terminal.panel.vw.*
|
||||||
import app.termora.terminal.panel.vw.VisualWindowManager
|
|
||||||
import com.formdev.flatlaf.util.SystemInfo
|
import com.formdev.flatlaf.util.SystemInfo
|
||||||
import org.apache.commons.lang3.ArrayUtils
|
import org.apache.commons.lang3.ArrayUtils
|
||||||
import org.apache.commons.lang3.StringUtils
|
import org.apache.commons.lang3.StringUtils
|
||||||
@@ -44,15 +45,15 @@ class TerminalPanel(val terminal: Terminal, private val writer: TerminalWriter)
|
|||||||
val SelectCopy = DataKey(Boolean::class)
|
val SelectCopy = DataKey(Boolean::class)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val properties get() = Database.getDatabase().properties
|
||||||
private val terminalBlink = TerminalBlink(terminal)
|
private val terminalBlink = TerminalBlink(terminal)
|
||||||
private val terminalFindPanel = TerminalFindPanel(this, terminal)
|
private val terminalFindPanel = TerminalFindPanel(this, terminal)
|
||||||
private val floatingToolbar = FloatingToolbarPanel()
|
private val floatingToolbar = FloatingToolbarPanel()
|
||||||
private val terminalDisplay = TerminalDisplay(this, terminal, terminalBlink)
|
private val terminalDisplay = TerminalDisplay(this, terminal, terminalBlink)
|
||||||
private val dataProviderSupport = DataProviderSupport()
|
|
||||||
private val layeredPane = TerminalLayeredPane()
|
private val layeredPane = TerminalLayeredPane()
|
||||||
private var visualWindows = emptyArray<VisualWindow>()
|
private var visualWindows = emptyArray<VisualWindow>()
|
||||||
|
|
||||||
val scrollBar = TerminalScrollBar(this@TerminalPanel, terminalFindPanel, terminal)
|
val scrollBar = TerminalScrollBar(this, terminalFindPanel, terminal)
|
||||||
var enableFloatingToolbar = true
|
var enableFloatingToolbar = true
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
@@ -63,6 +64,8 @@ class TerminalPanel(val terminal: Terminal, private val writer: TerminalWriter)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val dataProviderSupport = DataProviderSupport()
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 键盘事件
|
* 键盘事件
|
||||||
@@ -585,6 +588,37 @@ class TerminalPanel(val terminal: Terminal, private val writer: TerminalWriter)
|
|||||||
requestFocusInWindow()
|
requestFocusInWindow()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun resumeVisualWindows(id: String, dataProvider: DataProvider) {
|
||||||
|
val windows = properties.getString("VisualWindow.${id}.store") ?: return
|
||||||
|
for (name in windows.split(",")) {
|
||||||
|
if (name == "NVIDIA-SMI") {
|
||||||
|
addVisualWindow(
|
||||||
|
NvidiaSMIVisualWindow(
|
||||||
|
dataProvider.getData(DataProviders.TerminalTab) as SSHTerminalTab,
|
||||||
|
this
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else if (name == "SystemInformation") {
|
||||||
|
addVisualWindow(
|
||||||
|
SystemInformationVisualWindow(
|
||||||
|
dataProvider.getData(DataProviders.TerminalTab) as SSHTerminalTab,
|
||||||
|
this
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun storeVisualWindows(id: String) {
|
||||||
|
val windows = mutableListOf<String>()
|
||||||
|
for (window in getVisualWindows()) {
|
||||||
|
if (window is Resumeable) {
|
||||||
|
windows.add(window.getWindowName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
properties.putString("VisualWindow.${id}.store", windows.joinToString(","))
|
||||||
|
}
|
||||||
|
|
||||||
override fun getDimension(): Dimension {
|
override fun getDimension(): Dimension {
|
||||||
return Dimension(
|
return Dimension(
|
||||||
terminalDisplay.size.width + padding.left + padding.right,
|
terminalDisplay.size.width + padding.left + padding.right,
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ class NvidiaSMIVisualWindow(tab: SSHTerminalTab, visualWindowManager: VisualWind
|
|||||||
private val percentageBtn by lazy { JButton(if (isPercentage) Icons.text else Icons.percentage) }
|
private val percentageBtn by lazy { JButton(if (isPercentage) Icons.text else Icons.percentage) }
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
Disposer.register(tab, this)
|
||||||
initViews()
|
initViews()
|
||||||
initEvents()
|
initEvents()
|
||||||
initVisualWindowPanel()
|
initVisualWindowPanel()
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package app.termora.terminal.panel.vw
|
||||||
|
|
||||||
|
interface Resumeable
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package app.termora.terminal.panel.vw
|
package app.termora.terminal.panel.vw
|
||||||
|
|
||||||
import app.termora.Disposer
|
|
||||||
import app.termora.SSHTerminalTab
|
import app.termora.SSHTerminalTab
|
||||||
import app.termora.actions.AnActionEvent
|
import app.termora.actions.AnActionEvent
|
||||||
import app.termora.actions.DataProviders
|
import app.termora.actions.DataProviders
|
||||||
@@ -11,11 +10,7 @@ abstract class SSHVisualWindow(
|
|||||||
protected val tab: SSHTerminalTab,
|
protected val tab: SSHTerminalTab,
|
||||||
id: String,
|
id: String,
|
||||||
visualWindowManager: VisualWindowManager
|
visualWindowManager: VisualWindowManager
|
||||||
) : VisualWindowPanel(id, visualWindowManager) {
|
) : VisualWindowPanel(id, visualWindowManager), Resumeable {
|
||||||
|
|
||||||
init {
|
|
||||||
Disposer.register(tab, this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toggleWindow() {
|
override fun toggleWindow() {
|
||||||
val evt = AnActionEvent(tab.getJComponent(), StringUtils.EMPTY, EventObject(this))
|
val evt = AnActionEvent(tab.getJComponent(), StringUtils.EMPTY, EventObject(this))
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class SystemInformationVisualWindow(tab: SSHTerminalTab, visualWindowManager: Vi
|
|||||||
private val systemInformationPanel by lazy { SystemInformationPanel() }
|
private val systemInformationPanel by lazy { SystemInformationPanel() }
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
Disposer.register(tab, this)
|
||||||
initViews()
|
initViews()
|
||||||
initEvents()
|
initEvents()
|
||||||
initVisualWindowPanel()
|
initVisualWindowPanel()
|
||||||
@@ -137,7 +138,7 @@ class SystemInformationVisualWindow(tab: SSHTerminalTab, visualWindowManager: Vi
|
|||||||
private suspend fun refreshCPUAndMem(session: ClientSession) {
|
private suspend fun refreshCPUAndMem(session: ClientSession) {
|
||||||
|
|
||||||
// top
|
// top
|
||||||
var pair = SshClients.execChannel(session, "top -bn1")
|
val pair = SshClients.execChannel(session, "top -bn1")
|
||||||
if (pair.first != 0) {
|
if (pair.first != 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -236,7 +237,7 @@ class SystemInformationVisualWindow(tab: SSHTerminalTab, visualWindowManager: Vi
|
|||||||
private suspend fun refreshDisk(session: ClientSession) {
|
private suspend fun refreshDisk(session: ClientSession) {
|
||||||
|
|
||||||
// df -h
|
// df -h
|
||||||
var pair = SshClients.execChannel(session, "df -B1")
|
val pair = SshClients.execChannel(session, "df -B1")
|
||||||
if (pair.first != 0) {
|
if (pair.first != 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,4 +28,9 @@ interface VisualWindow : Disposable {
|
|||||||
* 切换独立模式
|
* 切换独立模式
|
||||||
*/
|
*/
|
||||||
fun toggleWindow()
|
fun toggleWindow()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同一个类,返回的相同
|
||||||
|
*/
|
||||||
|
fun getWindowName(): String
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package app.termora.terminal.panel.vw
|
package app.termora.terminal.panel.vw
|
||||||
|
|
||||||
|
import app.termora.actions.DataProvider
|
||||||
import java.awt.Dimension
|
import java.awt.Dimension
|
||||||
|
|
||||||
interface VisualWindowManager {
|
interface VisualWindowManager {
|
||||||
@@ -33,4 +34,14 @@ interface VisualWindowManager {
|
|||||||
* 获取管理器的宽高
|
* 获取管理器的宽高
|
||||||
*/
|
*/
|
||||||
fun getDimension(): Dimension
|
fun getDimension(): Dimension
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 恢复所有窗口
|
||||||
|
*/
|
||||||
|
fun resumeVisualWindows(id: String, dataProvider: DataProvider)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存储所有窗口
|
||||||
|
*/
|
||||||
|
fun storeVisualWindows(id: String)
|
||||||
}
|
}
|
||||||
@@ -374,4 +374,8 @@ open class VisualWindowPanel(protected val id: String, protected val visualWindo
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getWindowName(): String {
|
||||||
|
return id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user