diff --git a/src/main/kotlin/app/termora/HostTerminalTab.kt b/src/main/kotlin/app/termora/HostTerminalTab.kt
index 7cf8b26..4d629a5 100644
--- a/src/main/kotlin/app/termora/HostTerminalTab.kt
+++ b/src/main/kotlin/app/termora/HostTerminalTab.kt
@@ -3,14 +3,16 @@ package app.termora
import app.termora.actions.AnActionEvent
import app.termora.actions.DataProvider
import app.termora.actions.DataProviders
-import app.termora.terminal.*
+import app.termora.terminal.ControlCharacters
+import app.termora.terminal.DataKey
+import app.termora.terminal.DataListener
+import app.termora.terminal.Terminal
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.swing.Swing
import org.apache.commons.lang3.StringUtils
-import java.beans.PropertyChangeEvent
import java.util.*
import javax.swing.Icon
@@ -29,11 +31,6 @@ abstract class HostTerminalTab(
.getData(DataProviders.TerminalTabbedManager)
protected val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Swing)
protected val terminalModel get() = terminal.getTerminalModel()
- protected var unread = false
- set(value) {
- field = value
- firePropertyChange(PropertyChangeEvent(this, "icon", null, null))
- }
/* visualTerminal */
@@ -45,15 +42,6 @@ abstract class HostTerminalTab(
terminal.getTerminalModel().setData(Host, host)
terminal.getTerminalModel().addDataListener(object : DataListener {
override fun onChanged(key: DataKey<*>, data: Any) {
- if (key == VisualTerminal.Written) {
- if (hasFocus || unread) {
- return
- }
- // 如果当前选中的不是这个 Tab,那么设置成未读
- if (terminalTabbedManager?.getSelectedTerminalTab() != this@HostTerminalTab) {
- unread = true
- }
- }
}
})
}
@@ -75,8 +63,6 @@ abstract class HostTerminalTab(
override fun onGrabFocus() {
super.onGrabFocus()
- if (!unread) return
- unread = false
}
@Suppress("UNCHECKED_CAST")
diff --git a/src/main/kotlin/app/termora/MyTabbedPane.kt b/src/main/kotlin/app/termora/MyTabbedPane.kt
index d61fea0..1bc9e70 100644
--- a/src/main/kotlin/app/termora/MyTabbedPane.kt
+++ b/src/main/kotlin/app/termora/MyTabbedPane.kt
@@ -2,19 +2,20 @@ package app.termora
import app.termora.actions.AnActionEvent
import app.termora.actions.DataProviders
+import app.termora.actions.SwitchTabAction
+import app.termora.keymap.KeyShortcut
+import app.termora.keymap.KeymapManager
import com.formdev.flatlaf.extras.components.FlatTabbedPane
+import com.formdev.flatlaf.ui.FlatTabbedPaneUI
import org.apache.commons.lang3.StringUtils
import java.awt.*
import java.awt.event.*
import java.awt.image.BufferedImage
import java.util.*
-import javax.swing.ImageIcon
-import javax.swing.JDialog
-import javax.swing.JLabel
-import javax.swing.SwingUtilities
+import javax.swing.*
import kotlin.math.abs
-class MyTabbedPane : FlatTabbedPane() {
+class MyTabbedPane : FlatTabbedPane(), Disposable {
private val dragMouseAdaptor = DragMouseAdaptor()
private val terminalTabbedManager
@@ -23,6 +24,14 @@ class MyTabbedPane : FlatTabbedPane() {
private val owner
get() = AnActionEvent(this, StringUtils.EMPTY, EventObject(this))
.getData(DataProviders.TermoraFrame) as TermoraFrame
+ private val keymap get() = KeymapManager.getInstance().getActiveKeymap()
+ private var isSwitchTabMode = false
+ set(value) {
+ field = value
+ repaint()
+ }
+
+ private val isScreen get() = TermoraLayout.Layout == TermoraLayout.Screen
init {
isFocusable = false
@@ -38,6 +47,16 @@ class MyTabbedPane : FlatTabbedPane() {
private fun initEvents() {
addMouseListener(dragMouseAdaptor)
addMouseMotionListener(dragMouseAdaptor)
+
+ val awtEventListener = MyAWTEventListener()
+ toolkit.addAWTEventListener(awtEventListener, AWTEvent.KEY_EVENT_MASK or AWTEvent.WINDOW_EVENT_MASK)
+
+ Disposer.register(this, object : Disposable {
+ override fun dispose() {
+ toolkit.removeAWTEventListener(awtEventListener)
+ }
+ })
+
}
override fun processMouseEvent(e: MouseEvent) {
@@ -70,6 +89,29 @@ class MyTabbedPane : FlatTabbedPane() {
firePropertyChange("selectedIndex", oldIndex, index)
}
+ override fun updateUI() {
+ super.updateUI()
+ setUI(MyMyTabbedPaneUI())
+ }
+
+ private inner class MyAWTEventListener : AWTEventListener {
+ override fun eventDispatched(event: AWTEvent) {
+ if (event is KeyEvent) {
+ if (isSwitchTabMode) isSwitchTabMode = false
+ val shortcuts = keymap.getShortcut(SwitchTabAction.SWITCH_TAB)
+ if (shortcuts.isEmpty()) return
+ val shortcut = shortcuts.first() as KeyShortcut
+ val modifiers = KeyStroke.getKeyStroke(event.keyCode, event.modifiersEx).modifiers
+ if (shortcut.keyStroke.modifiers != modifiers) return
+ if (SwingUtilities.getWindowAncestor(event.component) != owner) return
+ if (isSwitchTabMode.not()) isSwitchTabMode = true
+ } else if (event is WindowEvent) {
+ if (event.id == WindowEvent.WINDOW_LOST_FOCUS || event.id == WindowEvent.WINDOW_DEACTIVATED) {
+ if (isSwitchTabMode) isSwitchTabMode = false
+ }
+ }
+ }
+ }
private inner class DragMouseAdaptor : MouseAdapter(), KeyEventDispatcher {
private var mousePressedPoint = Point()
@@ -267,5 +309,81 @@ class MyTabbedPane : FlatTabbedPane() {
}
}
+ private inner class MyMyTabbedPaneUI : FlatTabbedPaneUI() {
+ override fun paintIcon(
+ g: Graphics,
+ tabPlacement: Int,
+ tabIndex: Int,
+ icon: Icon,
+ iconRect: Rectangle?,
+ isSelected: Boolean
+ ) {
+ super.paintIcon(g, tabPlacement, tabIndex, MyIcon(icon, tabIndex, isSelected), iconRect, isSelected)
+ }
+
+
+ override fun createMoreTabsButton(): JButton {
+ return MyMoreTabsButton()
+ }
+
+ private inner class MyMoreTabsButton : FlatMoreTabsButton() {
+ override fun createTabMenuItem(tabIndex: Int): JMenuItem? {
+ val item = super.createTabMenuItem(tabIndex)
+ if (tabIndex == 0 && isScreen) {
+ item.text = Application.getName()
+ }
+ return item
+ }
+ }
+ }
+
+
+ override fun getIconAt(index: Int): Icon? {
+ if (isSwitchTabMode) {
+ return MyIcon(super.getIconAt(index), index, selectedIndex == index)
+ }
+ return super.getIconAt(index)
+ }
+
+ private inner class MyIcon(private val icon: Icon, private val tabIndex: Int, private val isSelected: Boolean) :
+ Icon {
+ override fun paintIcon(c: Component, g: Graphics, x: Int, y: Int) {
+ if (isScreen && tabIndex == 0) {
+ icon.paintIcon(c, g, x, y)
+ return
+ }
+
+ if (isSwitchTabMode.not()) {
+ icon.paintIcon(c, g, x, y)
+ return
+ }
+
+ if (g !is Graphics2D) return
+
+ g.save()
+ setupAntialiasing(g)
+
+ val fm = g.getFontMetrics(g.font)
+ val text = "${tabIndex + 1}"
+ val textWidth = fm.stringWidth(text)
+ val textHeight = fm.ascent
+
+ val centerX = x + (icon.iconWidth - textWidth) / 2
+ val centerY = y + (icon.iconHeight + textHeight) / 2 - 1
+
+ g.color = c.getForeground()
+ g.drawString(text, centerX, centerY)
+
+ g.restore()
+ }
+
+ override fun getIconWidth(): Int {
+ return icon.iconWidth
+ }
+
+ override fun getIconHeight(): Int {
+ return icon.iconHeight
+ }
+ }
}
\ No newline at end of file
diff --git a/src/main/kotlin/app/termora/TerminalTabbed.kt b/src/main/kotlin/app/termora/TerminalTabbed.kt
index 75fcec8..4cf0a9b 100644
--- a/src/main/kotlin/app/termora/TerminalTabbed.kt
+++ b/src/main/kotlin/app/termora/TerminalTabbed.kt
@@ -337,13 +337,7 @@ class TerminalTabbed(
val c = tab.getJComponent()
val title = (c.getClientProperty(titleProperty) ?: tab.getTitle()).toString()
- tabbedPane.insertTab(
- title,
- tab.getIcon(),
- c,
- StringUtils.EMPTY,
- index
- )
+ tabbedPane.insertTab(title, tab.getIcon(), c, StringUtils.EMPTY, index)
// 设置标题
c.putClientProperty(titleProperty, title)
diff --git a/src/main/kotlin/app/termora/TermoraFrame.kt b/src/main/kotlin/app/termora/TermoraFrame.kt
index 1764c37..902e2db 100644
--- a/src/main/kotlin/app/termora/TermoraFrame.kt
+++ b/src/main/kotlin/app/termora/TermoraFrame.kt
@@ -164,6 +164,8 @@ class TermoraFrame : JFrame(), DataProvider {
}).let { Disposer.register(windowScope, it) }
+ Disposer.register(windowScope, tabbedPane)
+
}
private fun initView() {
diff --git a/src/main/kotlin/app/termora/WelcomePanel.kt b/src/main/kotlin/app/termora/WelcomePanel.kt
index c5f4b20..8291e24 100644
--- a/src/main/kotlin/app/termora/WelcomePanel.kt
+++ b/src/main/kotlin/app/termora/WelcomePanel.kt
@@ -224,7 +224,7 @@ class WelcomePanel() : JPanel(BorderLayout()), Disposable, TerminalTab, DataProv
override fun getTitle(): String {
- return I18n.getString("termora.title")
+ return StringUtils.EMPTY
}
override fun getIcon(): Icon {
diff --git a/src/main/kotlin/app/termora/plugin/internal/local/LocalTerminalTab.kt b/src/main/kotlin/app/termora/plugin/internal/local/LocalTerminalTab.kt
index f42b694..519d292 100644
--- a/src/main/kotlin/app/termora/plugin/internal/local/LocalTerminalTab.kt
+++ b/src/main/kotlin/app/termora/plugin/internal/local/LocalTerminalTab.kt
@@ -31,7 +31,7 @@ class LocalTerminalTab(windowScope: WindowScope, host: Host) :
}
override fun getIcon(): Icon {
- return if (unread) Icons.terminalUnread else Icons.terminal
+ return Icons.terminal
}
override fun willBeClose(): Boolean {
diff --git a/src/main/kotlin/app/termora/plugin/internal/ssh/SSHTerminalTab.kt b/src/main/kotlin/app/termora/plugin/internal/ssh/SSHTerminalTab.kt
index 8dbbe42..83778cc 100644
--- a/src/main/kotlin/app/termora/plugin/internal/ssh/SSHTerminalTab.kt
+++ b/src/main/kotlin/app/termora/plugin/internal/ssh/SSHTerminalTab.kt
@@ -226,7 +226,7 @@ class SSHTerminalTab(
}
override fun getIcon(): Icon {
- return if (unread) Icons.terminalUnread else Icons.terminal
+ return Icons.terminal
}
override fun beforeClose() {
diff --git a/src/main/resources/icons/homeFolder.svg b/src/main/resources/icons/homeFolder.svg
index 93d056b..5d5a36e 100644
--- a/src/main/resources/icons/homeFolder.svg
+++ b/src/main/resources/icons/homeFolder.svg
@@ -1,4 +1,4 @@
diff --git a/src/main/resources/icons/homeFolder_dark.svg b/src/main/resources/icons/homeFolder_dark.svg
index 05fe1ec..205e307 100644
--- a/src/main/resources/icons/homeFolder_dark.svg
+++ b/src/main/resources/icons/homeFolder_dark.svg
@@ -1,4 +1,4 @@