chore: improve badge

This commit is contained in:
hstyi
2025-06-24 15:35:59 +08:00
committed by hstyi
parent efd01da6f1
commit 1c8abf9cba
11 changed files with 117 additions and 39 deletions

View File

@@ -1,18 +0,0 @@
package app.termora
import java.awt.Graphics2D
import javax.swing.JComponent
class BadgeGlassPaneExtension private constructor() : GlassPaneExtension {
companion object {
val instance = BadgeGlassPaneExtension()
}
override fun paint(c: JComponent, g2d: Graphics2D): Boolean {
return false
}
override fun ordered(): Long {
return 0
}
}

View File

@@ -9,11 +9,7 @@ import javax.swing.JComponent
*/
interface GlassPaneExtension : Extension {
/**
* 渲染背景,如果返回 true 会立即退出。(当有多个扩展的时候,只会执行一个)
*
* @return true渲染了背景false没有渲染背景
*/
fun paint(c: JComponent, g2d: Graphics2D): Boolean
fun paint(scope: WindowScope, c: JComponent, g2d: Graphics2D)
}

View File

@@ -43,5 +43,7 @@ fun Graphics2D.restore() {
fun setupAntialiasing(graphics: Graphics2D) {
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON)
graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY)
}

View File

@@ -253,7 +253,7 @@ class TermoraFrame : JFrame(), DataProvider {
}
private class GlassPane : JComponent() {
private inner class GlassPane : JComponent() {
init {
isFocusable = false
@@ -266,9 +266,8 @@ class TermoraFrame : JFrame(), DataProvider {
if (extensions.isNotEmpty()) {
for (extension in extensions) {
g2d.save()
val painted = extension.paint(this, g2d)
extension.paint(windowScope, this, g2d)
g2d.restore()
if (painted) break
}
}
}

View File

@@ -0,0 +1,41 @@
package app.termora.plugin.internal.badge
import app.termora.WindowScope
import java.awt.Color
import java.util.*
import javax.swing.JComponent
import javax.swing.UIManager
class Badge private constructor() {
companion object {
fun getInstance(scope: WindowScope): Badge {
return scope.getOrCreate(Badge::class) { Badge() }
}
}
private val map = WeakHashMap<JComponent, BadgePresentation>()
fun addBadge(component: JComponent): BadgePresentation {
val presentation = object : BadgePresentation {
override var visible: Boolean = true
override var color: Color = UIManager.getColor("Component.error.focusedBorderColor")
override fun dispose() {
removeBadge(component)
}
}
map[component] = presentation
return presentation
}
fun removeBadge(component: JComponent) {
map.remove(component)
}
fun getBadges(): Map<JComponent, BadgePresentation> {
return map.toMap()
}
}

View File

@@ -0,0 +1,41 @@
package app.termora.plugin.internal.badge
import app.termora.GlassPaneExtension
import app.termora.WindowScope
import app.termora.setupAntialiasing
import java.awt.Graphics2D
import javax.swing.JComponent
import javax.swing.SwingUtilities
class BadgeGlassPaneExtension private constructor() : GlassPaneExtension {
companion object {
val instance = BadgeGlassPaneExtension()
}
override fun paint(scope: WindowScope, c: JComponent, g2d: Graphics2D) {
val badges = Badge.getInstance(scope).getBadges()
if (badges.isEmpty()) return
setupAntialiasing(g2d)
for ((comp, presentation) in badges) {
if (comp.isShowing.not()) continue
if (presentation.visible.not()) continue
paintBadge(c, comp, g2d, presentation)
}
}
private fun paintBadge(root: JComponent, c: JComponent, g2d: Graphics2D, presentation: BadgePresentation) {
val point = c.locationOnScreen
SwingUtilities.convertPointFromScreen(point, root)
val size = 6
g2d.color = presentation.color
g2d.fillRoundRect(c.width - size - 4 + point.x, point.y + 4, size, size, size, size)
}
override fun ordered(): Long {
return Long.MAX_VALUE
}
}

View File

@@ -1,6 +1,5 @@
package app.termora.plugin.internal.badge
import app.termora.BadgeGlassPaneExtension
import app.termora.GlassPaneExtension
import app.termora.plugin.Extension
import app.termora.plugin.InternalPlugin

View File

@@ -0,0 +1,18 @@
package app.termora.plugin.internal.badge
import app.termora.Disposable
import java.awt.Color
interface BadgePresentation : Disposable {
/**
* 是否显示
*/
var visible: Boolean
/**
* 颜色
*/
var color: Color
}

View File

@@ -4,6 +4,7 @@ import app.termora.*
import app.termora.actions.AnAction
import app.termora.actions.AnActionEvent
import app.termora.actions.DataProviders
import app.termora.plugin.internal.badge.Badge
import app.termora.plugin.internal.ssh.SSHTerminalTab
import app.termora.plugin.internal.ssh.SSHTerminalTab.Companion.SSHSession
import app.termora.terminal.DataKey
@@ -61,8 +62,9 @@ class TransferVisualWindow(tab: SSHTerminalTab, visualWindowManager: VisualWindo
private val disposable = Disposer.newDisposable()
private val owner get() = SwingUtilities.getWindowAncestor(this)
private val questionBtn = JButton(Icons.questionMark)
private val badgeIcon = BadgeIcon(Icons.download)
private val downloadBtn = JButton(badgeIcon)
private val downloadBtn = JButton(Icons.download)
private val badgePresentation = Badge.getInstance(tab.windowScope)
.addBadge(downloadBtn).apply { visible = false }
init {
@@ -86,6 +88,7 @@ class TransferVisualWindow(tab: SSHTerminalTab, visualWindowManager: VisualWindo
Disposer.register(tab, this)
Disposer.register(this, disposable)
Disposer.register(disposable, transferManager)
Disposer.register(disposable, badgePresentation)
connectingPanel.busyLabel.isBusy = true
@@ -115,10 +118,10 @@ class TransferVisualWindow(tab: SSHTerminalTab, visualWindowManager: VisualWindo
transferManager.addTransferListener(object : TransferListener {
override fun onTransferCountChanged() {
val oldVisible = badgeIcon.visible
val oldVisible = badgePresentation.visible
val newVisible = transferManager.getTransferCount() > 0
if (oldVisible != newVisible) {
badgeIcon.visible = newVisible
badgePresentation.visible = newVisible
downloadBtn.repaint()
}
}