mirror of
https://github.com/TermoraDev/termora.git
synced 2026-01-16 02:12:58 +08:00
feat: support to set transparency (#446)
This commit is contained in:
@@ -421,6 +421,13 @@ class Database private constructor(private val env: Environment) : Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
protected inner class DoublePropertyDelegate(defaultValue: Double) :
|
||||
PropertyDelegate<Double>(defaultValue) {
|
||||
override fun convertValue(value: String): Double {
|
||||
return value.toDoubleOrNull() ?: initializer.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected inner class LongPropertyDelegate(defaultValue: Long) :
|
||||
PropertyDelegate<Long>(defaultValue) {
|
||||
@@ -632,6 +639,11 @@ class Database private constructor(private val env: Environment) : Disposable {
|
||||
I18n.containsLanguage(Locale.getDefault()) ?: Locale.US.toString()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 透明度
|
||||
*/
|
||||
var opacity by DoublePropertyDelegate(1.0)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
7
src/main/kotlin/app/termora/NotifyListener.kt
Normal file
7
src/main/kotlin/app/termora/NotifyListener.kt
Normal file
@@ -0,0 +1,7 @@
|
||||
package app.termora
|
||||
|
||||
import java.util.*
|
||||
|
||||
interface NotifyListener : EventListener {
|
||||
fun addNotify()
|
||||
}
|
||||
@@ -61,6 +61,7 @@ import java.nio.charset.StandardCharsets
|
||||
import java.util.*
|
||||
import java.util.function.Consumer
|
||||
import javax.swing.*
|
||||
import javax.swing.JSpinner.NumberEditor
|
||||
import javax.swing.event.DocumentEvent
|
||||
import javax.swing.event.PopupMenuEvent
|
||||
import javax.swing.event.PopupMenuListener
|
||||
@@ -131,6 +132,8 @@ class SettingsOptionsPane : OptionsPane() {
|
||||
val backgroundComBoBox = YesOrNoComboBox()
|
||||
val followSystemCheckBox = JCheckBox(I18n.getString("termora.settings.appearance.follow-system"))
|
||||
val preferredThemeBtn = JButton(Icons.settings)
|
||||
val opacitySpinner = NumberSpinner(100, 0, 100)
|
||||
|
||||
private val appearance get() = database.appearance
|
||||
|
||||
init {
|
||||
@@ -140,6 +143,21 @@ class SettingsOptionsPane : OptionsPane() {
|
||||
|
||||
private fun initView() {
|
||||
|
||||
backgroundComBoBox.isEnabled = SystemInfo.isWindows
|
||||
|
||||
opacitySpinner.isEnabled = SystemInfo.isMacOS || SystemInfo.isWindows
|
||||
opacitySpinner.model = object : SpinnerNumberModel(appearance.opacity, 0.1, 1.0, 0.1) {
|
||||
override fun getNextValue(): Any {
|
||||
return super.getNextValue() ?: maximum
|
||||
}
|
||||
|
||||
override fun getPreviousValue(): Any {
|
||||
return super.getPreviousValue() ?: minimum
|
||||
}
|
||||
}
|
||||
opacitySpinner.editor = NumberEditor(opacitySpinner, "#.##")
|
||||
opacitySpinner.model.stepSize = 0.05
|
||||
|
||||
followSystemCheckBox.isSelected = appearance.followSystem
|
||||
preferredThemeBtn.isEnabled = followSystemCheckBox.isSelected
|
||||
backgroundComBoBox.selectedItem = appearance.backgroundRunning
|
||||
@@ -179,6 +197,14 @@ class SettingsOptionsPane : OptionsPane() {
|
||||
}
|
||||
}
|
||||
|
||||
opacitySpinner.addChangeListener {
|
||||
val opacity = opacitySpinner.value
|
||||
if (opacity is Double) {
|
||||
TermoraFrameManager.getInstance().setOpacity(opacity)
|
||||
appearance.opacity = opacity
|
||||
}
|
||||
}
|
||||
|
||||
backgroundComBoBox.addItemListener {
|
||||
if (it.stateChange == ItemEvent.SELECTED) {
|
||||
appearance.backgroundRunning = backgroundComBoBox.selectedItem as Boolean
|
||||
@@ -283,7 +309,7 @@ class SettingsOptionsPane : OptionsPane() {
|
||||
private fun getFormPanel(): JPanel {
|
||||
val layout = FormLayout(
|
||||
"left:pref, $formMargin, default:grow, $formMargin, default, default:grow",
|
||||
"pref, $formMargin, pref, $formMargin, pref"
|
||||
"pref, $formMargin, pref, $formMargin, pref, $formMargin, pref"
|
||||
)
|
||||
val box = FlatToolBar()
|
||||
box.add(followSystemCheckBox)
|
||||
@@ -304,10 +330,12 @@ class SettingsOptionsPane : OptionsPane() {
|
||||
}
|
||||
})).xy(5, rows).apply { rows += step }
|
||||
|
||||
if (SystemInfo.isWindows) {
|
||||
builder.add("${I18n.getString("termora.settings.appearance.background-running")}:").xy(1, rows)
|
||||
.add(backgroundComBoBox).xy(3, rows)
|
||||
}
|
||||
|
||||
builder.add("${I18n.getString("termora.settings.appearance.opacity")}:").xy(1, rows)
|
||||
.add(opacitySpinner).xy(3, rows).apply { rows += step }
|
||||
|
||||
builder.add("${I18n.getString("termora.settings.appearance.background-running")}:").xy(1, rows)
|
||||
.add(backgroundComBoBox).xy(3, rows)
|
||||
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import app.termora.terminal.DataKey
|
||||
import com.formdev.flatlaf.FlatClientProperties
|
||||
import com.formdev.flatlaf.util.SystemInfo
|
||||
import com.jetbrains.JBR
|
||||
import org.apache.commons.lang3.ArrayUtils
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.Dimension
|
||||
import java.awt.Insets
|
||||
@@ -24,6 +25,7 @@ import javax.swing.SwingUtilities
|
||||
import javax.swing.SwingUtilities.isEventDispatchThread
|
||||
import javax.swing.UIManager
|
||||
|
||||
|
||||
fun assertEventDispatchThread() {
|
||||
if (!isEventDispatchThread()) throw WrongThreadException("AWT EventQueue")
|
||||
}
|
||||
@@ -41,6 +43,7 @@ class TermoraFrame : JFrame(), DataProvider {
|
||||
private val welcomePanel = WelcomePanel(windowScope)
|
||||
private val sftp get() = Database.getDatabase().sftp
|
||||
private val myUI = MyFlatRootPaneUI()
|
||||
private var notifyListeners = emptyArray<NotifyListener>()
|
||||
|
||||
|
||||
init {
|
||||
@@ -239,4 +242,16 @@ class TermoraFrame : JFrame(), DataProvider {
|
||||
return id.hashCode()
|
||||
}
|
||||
|
||||
fun addNotifyListener(listener: NotifyListener) {
|
||||
notifyListeners += listener
|
||||
}
|
||||
|
||||
fun removeNotifyListener(listener: NotifyListener) {
|
||||
notifyListeners = ArrayUtils.removeElements(notifyListeners, listener)
|
||||
}
|
||||
|
||||
override fun addNotify() {
|
||||
super.addNotify()
|
||||
notifyListeners.forEach { it.addNotify() }
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,18 @@
|
||||
package app.termora
|
||||
|
||||
import app.termora.native.osx.NativeMacLibrary
|
||||
import com.formdev.flatlaf.ui.FlatNativeWindowsLibrary
|
||||
import com.formdev.flatlaf.util.SystemInfo
|
||||
import com.sun.jna.Pointer
|
||||
import com.sun.jna.platform.win32.User32
|
||||
import com.sun.jna.platform.win32.WinDef
|
||||
import com.sun.jna.platform.win32.WinUser.*
|
||||
import de.jangassen.jfa.ThreadUtils
|
||||
import de.jangassen.jfa.foundation.Foundation
|
||||
import de.jangassen.jfa.foundation.ID
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.awt.Frame
|
||||
import java.awt.Window
|
||||
import java.awt.event.WindowAdapter
|
||||
import java.awt.event.WindowEvent
|
||||
import javax.swing.JFrame
|
||||
@@ -13,6 +23,7 @@ import javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE
|
||||
import kotlin.math.max
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
|
||||
class TermoraFrameManager {
|
||||
|
||||
companion object {
|
||||
@@ -51,6 +62,15 @@ class TermoraFrameManager {
|
||||
}
|
||||
}
|
||||
|
||||
frame.addNotifyListener(object : NotifyListener {
|
||||
private val opacity get() = Database.getDatabase().appearance.opacity
|
||||
override fun addNotify() {
|
||||
val opacity = this.opacity
|
||||
if (opacity >= 1.0) return
|
||||
setOpacity(frame, opacity)
|
||||
}
|
||||
})
|
||||
|
||||
return frame.apply { frames.add(this) }
|
||||
}
|
||||
|
||||
@@ -153,6 +173,31 @@ class TermoraFrameManager {
|
||||
return FrameRectangle(x, y, w, h, s)
|
||||
}
|
||||
|
||||
fun setOpacity(opacity: Double) {
|
||||
if (opacity < 0 || opacity > 1 || SystemInfo.isLinux) return
|
||||
for (window in getWindows()) {
|
||||
setOpacity(window, opacity)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setOpacity(window: Window, opacity: Double) {
|
||||
if (SystemInfo.isMacOS) {
|
||||
val nsWindow = ID(NativeMacLibrary.getNSWindow(window) ?: return)
|
||||
ThreadUtils.dispatch_async {
|
||||
Foundation.invoke(nsWindow, "setOpaque:", false)
|
||||
Foundation.invoke(nsWindow, "setAlphaValue:", opacity)
|
||||
}
|
||||
} else if (SystemInfo.isWindows) {
|
||||
val alpha = ((opacity * 255).toInt() and 0xFF).toByte()
|
||||
val hwnd = WinDef.HWND(Pointer.createConstant(FlatNativeWindowsLibrary.getHWND(window)))
|
||||
val exStyle = User32.INSTANCE.GetWindowLong(hwnd, User32.GWL_EXSTYLE)
|
||||
if (exStyle and WS_EX_LAYERED == 0) {
|
||||
User32.INSTANCE.SetWindowLong(hwnd, GWL_EXSTYLE, exStyle or WS_EX_LAYERED)
|
||||
}
|
||||
User32.INSTANCE.SetLayeredWindowAttributes(hwnd, 0, alpha, LWA_ALPHA)
|
||||
}
|
||||
}
|
||||
|
||||
private data class FrameRectangle(
|
||||
val x: Int, val y: Int, val w: Int, val h: Int, val s: Int
|
||||
) {
|
||||
|
||||
@@ -146,7 +146,7 @@ open class EmailFormattedTextField(var maxLength: Int = Int.MAX_VALUE) : Outline
|
||||
}
|
||||
|
||||
|
||||
abstract class NumberSpinner(
|
||||
open class NumberSpinner(
|
||||
value: Int,
|
||||
minimum: Int,
|
||||
maximum: Int,
|
||||
|
||||
@@ -54,6 +54,7 @@ termora.settings.appearance.theme=Theme
|
||||
termora.settings.appearance.language=Language
|
||||
termora.settings.appearance.i-want-to-translate=I want to translate
|
||||
termora.settings.appearance.follow-system=Sync with OS
|
||||
termora.settings.appearance.opacity=Opacity
|
||||
termora.settings.appearance.background-running=Backgrounding
|
||||
|
||||
termora.setting.security=Security
|
||||
|
||||
@@ -51,6 +51,7 @@ termora.settings.appearance.theme=主题
|
||||
termora.settings.appearance.language=语言
|
||||
termora.settings.appearance.i-want-to-translate=我想要翻译
|
||||
termora.settings.appearance.follow-system=跟随系统
|
||||
termora.settings.appearance.opacity=透明度
|
||||
termora.settings.appearance.background-running=后台运行
|
||||
|
||||
termora.setting.security=安全
|
||||
|
||||
@@ -52,6 +52,7 @@ termora.settings.appearance.theme=主题
|
||||
termora.settings.appearance.language=語言
|
||||
termora.settings.appearance.i-want-to-translate=我想要翻譯
|
||||
termora.settings.appearance.follow-system=跟隨系統
|
||||
termora.settings.appearance.opacity=透明度
|
||||
termora.settings.appearance.background-running=後台運行
|
||||
|
||||
termora.setting.security=安全
|
||||
|
||||
Reference in New Issue
Block a user