From 9646a98f6d90a1e8fd6670c928133dd0e447cdae Mon Sep 17 00:00:00 2001 From: hstyi Date: Fri, 8 Aug 2025 15:03:05 +0800 Subject: [PATCH] feat: background image supports fill mode --- plugins/bg/build.gradle.kts | 2 +- .../app/termora/plugins/bg/Appearance.kt | 4 ++ .../plugins/bg/BGGlassPaneExtension.kt | 43 +++++++++++++++- .../termora/plugins/bg/BackgroundOption.kt | 50 ++++++++++++++++++- .../kotlin/app/termora/plugins/bg/FillMode.kt | 8 +++ .../main/resources/i18n/messages.properties | 5 ++ .../resources/i18n/messages_zh_CN.properties | 6 +++ .../resources/i18n/messages_zh_TW.properties | 6 +++ 8 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 plugins/bg/src/main/kotlin/app/termora/plugins/bg/FillMode.kt diff --git a/plugins/bg/build.gradle.kts b/plugins/bg/build.gradle.kts index 2397fed..840903d 100644 --- a/plugins/bg/build.gradle.kts +++ b/plugins/bg/build.gradle.kts @@ -3,7 +3,7 @@ plugins { } -project.version = "0.0.5" +project.version = "0.0.6" diff --git a/plugins/bg/src/main/kotlin/app/termora/plugins/bg/Appearance.kt b/plugins/bg/src/main/kotlin/app/termora/plugins/bg/Appearance.kt index 5bfade7..d325d21 100644 --- a/plugins/bg/src/main/kotlin/app/termora/plugins/bg/Appearance.kt +++ b/plugins/bg/src/main/kotlin/app/termora/plugins/bg/Appearance.kt @@ -18,4 +18,8 @@ object Appearance { set(value) { enableManager.setFlag("Plugins.bg.interval", value) } + + var fillMode: String + get() = enableManager.getFlag("Plugins.bg.fillMode", FillMode.STRETCH.name) + set(value) = enableManager.setFlag("Plugins.bg.fillMode", value) } \ No newline at end of file diff --git a/plugins/bg/src/main/kotlin/app/termora/plugins/bg/BGGlassPaneExtension.kt b/plugins/bg/src/main/kotlin/app/termora/plugins/bg/BGGlassPaneExtension.kt index 800aa2f..262cdad 100644 --- a/plugins/bg/src/main/kotlin/app/termora/plugins/bg/BGGlassPaneExtension.kt +++ b/plugins/bg/src/main/kotlin/app/termora/plugins/bg/BGGlassPaneExtension.kt @@ -2,6 +2,8 @@ package app.termora.plugins.bg import app.termora.GlassPaneExtension import app.termora.WindowScope +import app.termora.restore +import app.termora.save import com.formdev.flatlaf.FlatLaf import java.awt.AlphaComposite import java.awt.Graphics2D @@ -12,15 +14,52 @@ class BGGlassPaneExtension private constructor() : GlassPaneExtension { val instance = BGGlassPaneExtension() } + override fun paint(scope: WindowScope, c: JComponent, g2d: Graphics2D) { val img = BackgroundManager.getInstance().getBackgroundImage() ?: return + g2d.save() g2d.composite = AlphaComposite.getInstance( AlphaComposite.SRC_OVER, if (FlatLaf.isLafDark()) 0.2f else 0.1f ) - g2d.drawImage(img, 0, 0, c.width, c.height, null) - g2d.composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER) + + when (Appearance.fillMode) { + FillMode.STRETCH.name -> { + g2d.drawImage(img, 0, 0, c.width, c.height, null) + } + + FillMode.CENTER.name -> { + val x = (c.width - img.width) / 2 + val y = (c.height - img.height) / 2 + g2d.drawImage(img, x, y, null) + } + + FillMode.TILE.name -> { + val iw = img.width + val ih = img.height + var y = 0 + while (y < c.height) { + var x = 0 + while (x < c.width) { + g2d.drawImage(img, x, y, null) + x += iw + } + y += ih + } + } + + FillMode.FIT.name -> { + val scale = maxOf(c.width.toDouble() / img.width, c.height.toDouble() / img.height) + val newW = (img.width * scale).toInt() + val newH = (img.height * scale).toInt() + val x = (c.width - newW) / 2 + val y = (c.height - newH) / 2 + g2d.drawImage(img, x, y, newW, newH, null) + } + } + + g2d.restore() } } \ No newline at end of file diff --git a/plugins/bg/src/main/kotlin/app/termora/plugins/bg/BackgroundOption.kt b/plugins/bg/src/main/kotlin/app/termora/plugins/bg/BackgroundOption.kt index bcbbaab..85d2f10 100644 --- a/plugins/bg/src/main/kotlin/app/termora/plugins/bg/BackgroundOption.kt +++ b/plugins/bg/src/main/kotlin/app/termora/plugins/bg/BackgroundOption.kt @@ -10,6 +10,8 @@ import org.apache.commons.lang3.StringUtils import org.apache.commons.lang3.exception.ExceptionUtils import org.slf4j.LoggerFactory import java.awt.BorderLayout +import java.awt.Component +import java.awt.event.ItemEvent import java.io.File import java.nio.file.StandardCopyOption import javax.swing.* @@ -23,6 +25,7 @@ class BackgroundOption : JPanel(BorderLayout()), OptionsPane.PluginOption { private val owner get() = SwingUtilities.getWindowAncestor(this) val backgroundImageTextField = OutlineTextField() + val fillModeComboBox = OutlineComboBox() val intervalSpinner = NumberSpinner(360, minimum = 30, maximum = 86400) private val backgroundButton = JButton(Icons.folder) @@ -36,6 +39,38 @@ class BackgroundOption : JPanel(BorderLayout()), OptionsPane.PluginOption { private fun initView() { + fillModeComboBox.addItem(FillMode.STRETCH) + fillModeComboBox.addItem(FillMode.FIT) + fillModeComboBox.addItem(FillMode.CENTER) + fillModeComboBox.addItem(FillMode.TILE) + + fillModeComboBox.selectedItem = runCatching { FillMode.valueOf(Appearance.fillMode) } + .getOrNull() ?: FillMode.STRETCH + + fillModeComboBox.renderer = object : DefaultListCellRenderer() { + override fun getListCellRendererComponent( + list: JList<*>?, + value: Any?, + index: Int, + isSelected: Boolean, + cellHasFocus: Boolean + ): Component? { + var text = value?.toString() + + if (value == FillMode.STRETCH) { + text = BGI18n.getString("termora.plugins.bg.fill-mode.stretch") + } else if (value == FillMode.FIT) { + text = BGI18n.getString("termora.plugins.bg.fill-mode.fit") + } else if (value == FillMode.CENTER) { + text = BGI18n.getString("termora.plugins.bg.fill-mode.center") + } else if (value == FillMode.TILE) { + text = BGI18n.getString("termora.plugins.bg.fill-mode.tile") + } + + return super.getListCellRendererComponent(list, text, index, isSelected, cellHasFocus) + } + } + backgroundImageTextField.isEditable = false backgroundImageTextField.trailingComponent = backgroundButton backgroundImageTextField.text = Appearance.backgroundImage @@ -80,6 +115,15 @@ class BackgroundOption : JPanel(BorderLayout()), OptionsPane.PluginOption { Appearance.interval = value } } + + fillModeComboBox.addItemListener { + if (it.stateChange == ItemEvent.SELECTED) { + Appearance.fillMode = fillModeComboBox.selectedItem?.toString() ?: FillMode.STRETCH.name + for (frame in TermoraFrameManager.getInstance().getWindows()) { + SwingUtilities.invokeLater { SwingUtilities.updateComponentTreeUI(frame) } + } + } + } } private fun onSelectedBackgroundImage(file: File) { @@ -124,7 +168,7 @@ class BackgroundOption : JPanel(BorderLayout()), OptionsPane.PluginOption { private fun getFormPanel(): JPanel { val layout = FormLayout( "left:pref, $FORM_MARGIN, default:grow, $FORM_MARGIN, default", - "pref, $FORM_MARGIN, pref" + "pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref" ) var rows = 1 @@ -138,6 +182,10 @@ class BackgroundOption : JPanel(BorderLayout()), OptionsPane.PluginOption { .add(bgClearBox).xy(5, rows) .apply { rows += step } + builder.add("${BGI18n.getString("termora.plugins.bg.fill-mode")}:").xy(1, rows) + .add(fillModeComboBox).xy(3, rows) + .apply { rows += step } + builder.add("${BGI18n.getString("termora.plugins.bg.interval")}:").xy(1, rows) .add(intervalSpinner).xy(3, rows) .apply { rows += step } diff --git a/plugins/bg/src/main/kotlin/app/termora/plugins/bg/FillMode.kt b/plugins/bg/src/main/kotlin/app/termora/plugins/bg/FillMode.kt new file mode 100644 index 0000000..6c56b0d --- /dev/null +++ b/plugins/bg/src/main/kotlin/app/termora/plugins/bg/FillMode.kt @@ -0,0 +1,8 @@ +package app.termora.plugins.bg + +enum class FillMode { + STRETCH, // 拉伸 + FIT, // 等比例铺满 + CENTER, // 居中 + TILE, // 平铺 +} \ No newline at end of file diff --git a/plugins/bg/src/main/resources/i18n/messages.properties b/plugins/bg/src/main/resources/i18n/messages.properties index 58f334c..3c54af8 100644 --- a/plugins/bg/src/main/resources/i18n/messages.properties +++ b/plugins/bg/src/main/resources/i18n/messages.properties @@ -1,2 +1,7 @@ termora.plugins.bg.interval=Interval +termora.plugins.bg.fill-mode=Fill Mode +termora.plugins.bg.fill-mode.stretch=Stretch +termora.plugins.bg.fill-mode.fit=Fit +termora.plugins.bg.fill-mode.center=Center +termora.plugins.bg.fill-mode.tile=Tile termora.plugins.bg.background-image=Background Image diff --git a/plugins/bg/src/main/resources/i18n/messages_zh_CN.properties b/plugins/bg/src/main/resources/i18n/messages_zh_CN.properties index 5755aa2..79008b1 100644 --- a/plugins/bg/src/main/resources/i18n/messages_zh_CN.properties +++ b/plugins/bg/src/main/resources/i18n/messages_zh_CN.properties @@ -1,2 +1,8 @@ termora.plugins.bg.background-image=背景图 termora.plugins.bg.interval=切换间隔 + +termora.plugins.bg.fill-mode=填充模式 +termora.plugins.bg.fill-mode.stretch=拉伸 +termora.plugins.bg.fill-mode.fit=适合 +termora.plugins.bg.fill-mode.center=居中 +termora.plugins.bg.fill-mode.tile=平铺 diff --git a/plugins/bg/src/main/resources/i18n/messages_zh_TW.properties b/plugins/bg/src/main/resources/i18n/messages_zh_TW.properties index b8be3ce..81ac5d3 100644 --- a/plugins/bg/src/main/resources/i18n/messages_zh_TW.properties +++ b/plugins/bg/src/main/resources/i18n/messages_zh_TW.properties @@ -1,2 +1,8 @@ termora.plugins.bg.background-image=背景圖 termora.plugins.bg.interval=切換間隔 + +termora.plugins.bg.fill-mode=填充模式 +termora.plugins.bg.fill-mode.stretch=拉伸 +termora.plugins.bg.fill-mode.fit=適合 +termora.plugins.bg.fill-mode.center=居中 +termora.plugins.bg.fill-mode.tile=平鋪