add system tray for linux (#346)

### Fixed

- System Tray in linux not showing well
This commit is contained in:
AmirHossein Abdolmotallebi 2025-01-05 20:07:20 +03:30 committed by GitHub
parent 40ca2a51fc
commit 53ff9154f8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 128 additions and 6 deletions

View File

@ -65,11 +65,10 @@ dependencies {
implementation(project(":desktop:tray:common")) implementation(project(":desktop:tray:common"))
if (Platform.getCurrentPlatform() == Platform.Desktop.Windows) { if (Platform.getCurrentPlatform() == Platform.Desktop.Windows) {
implementation(project(":desktop:tray:windows")) implementation(project(":desktop:tray:linux"))
// implementation(project(":desktop:tray:windows"))
} else { } else {
// TODO use external library for linux and remove this line implementation(project(":desktop:tray:linux"))
implementation(project(":desktop:tray:windows"))
// implementation(project(":desktop:tray:linux"))
} }
implementation(project(":shared:app")) implementation(project(":shared:app"))

View File

@ -7,4 +7,5 @@ dependencies {
api(project(":desktop:tray:common")) api(project(":desktop:tray:common"))
ksp(libs.autoService.ksp) ksp(libs.autoService.ksp)
implementation(libs.autoService.annoations) implementation(libs.autoService.annoations)
implementation(libs.systemTray)
} }

View File

@ -1,14 +1,134 @@
package ir.amirab.util.desktop.systemtray.impl package ir.amirab.util.desktop.systemtray.impl
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.toAwtImage
import com.google.auto.service.AutoService import com.google.auto.service.AutoService
import dorkbox.systemTray.Menu
import dorkbox.systemTray.Separator
import dorkbox.systemTray.SystemTray
import ir.amirab.util.compose.IconSource import ir.amirab.util.compose.IconSource
import ir.amirab.util.compose.StringSource import ir.amirab.util.compose.StringSource
import ir.amirab.util.compose.action.MenuItem import ir.amirab.util.compose.action.MenuItem
import ir.amirab.util.desktop.GlobalDensity
import ir.amirab.util.desktop.GlobalLayoutDirection
import ir.amirab.util.desktop.systemtray.IComposeSystemTray import ir.amirab.util.desktop.systemtray.IComposeSystemTray
import java.awt.Image
@AutoService(IComposeSystemTray::class) @AutoService(IComposeSystemTray::class)
class ComposeSystemTrayForLinux : IComposeSystemTray { class ComposeSystemTrayForLinux : IComposeSystemTray {
override fun ComposeSystemTray(icon: IconSource, title: StringSource, menu: List<MenuItem>, onClick: () -> Unit) { @Composable
TODO("Not yet implemented") override fun ComposeSystemTray(
icon: IconSource,
title: StringSource,
menu: List<MenuItem>,
onClick: () -> Unit
) {
val systemTray: SystemTray? = remember { SystemTray.get() }
if (systemTray == null) {
System.err.println("System tray is not supported")
return
}
val iconPainter = icon.rememberPainter()
val awtImage = remember(iconPainter) {
iconPainter.toAwtImage(
GlobalDensity,
GlobalLayoutDirection,
)
}
val tooltipString = title.rememberString()
LaunchedEffect(awtImage) {
systemTray.setImage(awtImage)
}
LaunchedEffect(tooltipString) {
systemTray.setTooltip(tooltipString)
}
val immutableMenu = menu.toImmutableMenuItem()
DisposableEffect(immutableMenu) {
systemTray.menu.addImmutableMenu(immutableMenu)
onDispose {
for (entry in systemTray.menu.entries) {
systemTray.menu.remove(entry)
}
}
}
DisposableEffect(Unit) {
onDispose {
systemTray.shutdown()
}
}
} }
}
private fun Menu.addImmutableMenu(immutableMenu: List<ImmutableMenuItem>) {
for (item in immutableMenu) {
when (item) {
ImmutableMenuItem.Separator -> add(Separator())
is ImmutableMenuItem.SingleItem -> add(
dorkbox.systemTray.MenuItem(
item.title,
item.icon,
{
item.onAction()
}
)
)
is ImmutableMenuItem.SubMenu -> add(
Menu(
item.title,
item.icon,
).apply {
addImmutableMenu(item.items)
}
)
}
}
}
@Composable
private fun List<MenuItem>.toImmutableMenuItem(): List<ImmutableMenuItem> {
val density = GlobalDensity
val layoutDirection = GlobalLayoutDirection
return map {
when (it) {
MenuItem.Separator -> ImmutableMenuItem.Separator
is MenuItem.SingleItem -> {
ImmutableMenuItem.SingleItem(
title = it.title.collectAsState().value.rememberString(),
icon = null,
// icon = it.icon.collectAsState().value?.rememberPainter()?.toAwtImage(
// density, layoutDirection,
// ),
onAction = it::onClick
)
}
is MenuItem.SubMenu -> ImmutableMenuItem.SubMenu(
title = it.title.collectAsState().value.rememberString(),
icon = it.icon.collectAsState().value?.rememberPainter()?.toAwtImage(
GlobalDensity, GlobalLayoutDirection,
),
items = it.items.collectAsState().value.toImmutableMenuItem()
)
}
}
}
@Immutable
internal sealed class ImmutableMenuItem {
data object Separator : ImmutableMenuItem()
data class SingleItem(
val title: String,
val icon: Image?,
val onAction: () -> Unit,
) : ImmutableMenuItem()
data class SubMenu(
val title: String,
val icon: Image?,
val items: List<ImmutableMenuItem>
) : ImmutableMenuItem()
} }

View File

@ -128,6 +128,8 @@ kotlinFileWatcher = { module = "io.github.irgaly.kfswatch:kfswatch", version.ref
autoService-ksp = { module = "dev.zacsweers.autoservice:auto-service-ksp", version.ref = "autoServiceKsp" } autoService-ksp = { module = "dev.zacsweers.autoservice:auto-service-ksp", version.ref = "autoServiceKsp" }
autoService-annoations = { module = "com.google.auto.service:auto-service-annotations", version.ref = "autoService" } autoService-annoations = { module = "com.google.auto.service:auto-service-annotations", version.ref = "autoService" }
systemTray = "com.dorkbox:SystemTray:4.4"
[plugins] [plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
compose = { id = "org.jetbrains.compose", version.ref = "compose" } compose = { id = "org.jetbrains.compose", version.ref = "compose" }