Apps ancrées au tray
TrayApp ancre une fenêtre popup Compose à une icône du tray système pour construire des apps menu bar en Kotlin.
TrayApp ancre une fenêtre Compose complète à une icône du tray système : vous cliquez gauche sur
l'icône et la fenêtre s'ouvre à côté, vous cliquez ailleurs et elle se ferme. C'est le pattern menu
bar app utilisé par des outils comme Bartender, iStat Menus et Hidden Bar.
Depuis 2.1.0, TrayApp vit dans composenativetray-app, pas dans l'artefact tray de base.
Ajouter la dépendance
dependencies {
implementation("dev.nucleusframework:composenativetray-app:2.1.6")
}composenativetray-app tire composenativetray et le backend de fenêtre
Tao de Nucleus. TrayApp est une extension de NucleusApplicationScope — lancez avec
nucleusApplication { }.
Dernière version: 2.1.6
Créer une tray app
Passez une icône, un tooltip, une taille de fenêtre et le contenu Compose à afficher dans la popup :
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Dashboard
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import dev.nucleusframework.application.nucleusApplication
import dev.nucleusframework.composenativetray.trayapp.TrayApp
fun main() = nucleusApplication {
TrayApp(
icon = Icons.Default.Dashboard,
tooltip = "Quick dashboard",
windowSize = DpSize(300.dp, 400.dp),
) {
Column(Modifier.fillMaxSize().padding(16.dp)) {
Text("Dashboard", style = MaterialTheme.typography.h6)
Spacer(Modifier.height(8.dp))
Text("CPU: 42%")
Text("RAM: 8.2 GB")
}
}
}Fonctionnement
TrayApp combine une icône tray et une fenêtre Compose transparente, sans décoration et toujours
au-dessus. La bibliothèque suit la position de l'icône à l'écran et place la fenêtre à côté avec
l'offset adapté à chaque système d'exploitation.
Le clic-en-dehors-pour-fermer est branché sur l'événement de perte de focus de l'OS : la popup se ferme comme les fenêtres menu bar natives de chaque bureau.
Piloter la visibilité et la taille avec l'état
Conservez un TrayAppState pour afficher, masquer, redimensionner ou changer le mode de dismiss
depuis votre propre code :
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Dashboard
import androidx.compose.foundation.layout.Column
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import dev.nucleusframework.application.nucleusApplication
import dev.nucleusframework.composenativetray.trayapp.TrayApp
import dev.nucleusframework.composenativetray.trayapp.TrayWindowDismissMode
import dev.nucleusframework.composenativetray.trayapp.rememberTrayAppState
fun main() = nucleusApplication {
val state = rememberTrayAppState(
initialWindowSize = DpSize(350.dp, 500.dp),
initiallyVisible = false,
initialDismissMode = TrayWindowDismissMode.AUTO,
)
TrayApp(
icon = Icons.Default.Dashboard,
tooltip = "Dashboard",
state = state,
) {
Column {
Text("Dashboard")
Button(onClick = { state.hide() }) { Text("Fermer") }
Button(onClick = { state.setWindowSize(500.dp, 600.dp) }) { Text("Redimensionner") }
}
}
}Ajouter un menu contextuel
Le clic gauche ouvre la popup ; le clic droit ouvre un menu classique :
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Dashboard
import dev.nucleusframework.application.nucleusApplication
import dev.nucleusframework.composenativetray.trayapp.TrayApp
import kotlin.system.exitProcess
fun main() = nucleusApplication {
TrayApp(
icon = Icons.Default.Dashboard,
tooltip = "Dashboard",
menu = {
Item(label = "Réglages") { openSettings() }
Divider()
Item(label = "Quitter") { exitProcess(0) }
},
) {
Text("Contenu de la popup")
}
}Masquer l'icône du Dock sur macOS
Sur le backend Tao, ne définissez pas LSUIElement dans l'Info.plist. Passez
dockIconFollowsWindows = true à nucleusApplication :
fun main() = nucleusApplication(dockIconFollowsWindows = true) {
TrayApp(
icon = Icons.Default.Dashboard,
tooltip = "Dashboard",
) {
Text("Contenu de la popup")
}
}L'app démarre en accessoire (pas d'icône Dock, pas de barre de menus). Une tuile Dock n'apparaît
que tant qu'au moins une DecoratedWindow avec hiddenFromDock = false est visible. Les popups
TrayApp sont des panneaux autonomes et ne comptent jamais : une app tray-only reste hors du
Dock. Le flag est ignoré hors macOS et sur le backend AWT. Voir
Masquer de la barre des tâches / du Dock.
Référence de l'API
Options de fenêtre de TrayApp
| Paramètre | Défaut | Notes |
|---|---|---|
windowSize | DpSize(300.dp, 200.dp) | taille initiale |
visibleOnStart | false | affiche tout de suite |
enterTransition / exitTransition | défaut plateforme | animations |
undecorated | true | sans cadre |
resizable | false | redimensionnement utilisateur |
windowsTitle | "" | titre de fenêtre (utilisé sous Linux et en mode décoré) |
horizontalOffset / verticalOffset | 0 / défaut plateforme | décalage depuis l'icône |
TrayAppState
| API | Description |
|---|---|
isVisible: StateFlow<Boolean> | visibilité courante |
show() / hide() / toggle() | contrôle impératif |
setWindowSize(size) | redimensionne à la volée |
setDismissMode(mode) | AUTO (clic dehors ferme) ou MANUAL |
onVisibilityChanged(cb) | observer les transitions |
Notes
- Depuis 2.1.0,
getTrayPosition()etTrayPositionvivent danscomposenativetray-app(dev.nucleusframework.composenativetray.trayapp). Ils ont besoin du backend Tao pour la géométrie d'écran. PréférezTrayAppsi vous voulez seulement une popup à côté de l'icône. - La popup est un panneau natif standalone, pas une
DecoratedWindow. Les fichiers déposés atteignentdragAndDropTargetCompose comme une fenêtre normale (Windows et macOS depuis 2.5.3, Linux depuis 2.5.4). Le scroll molette et trackpad utilise le même mapping AWT que la fenêtre principale (Windows depuis 2.5.2, macOS depuis 2.5.4, Linux depuis 2.5.6). Une popup Windows masquée ne soumet plus de frames GPU (2.5.2). Une frame planifiée depuis l'intérieur d'une passe de scène — un drag de scrollbar qui sous-compose de nouveaux items lazy, par exemple — est postée sur la file principale au lieu d'être rendue en ligne, et ne peut donc pas ré-entrer dans measure et layout (2.5.7). - Sous macOS, le panneau est stationnaire et présent sur tous les espaces : « Cliquer sur le fond
d'écran pour révéler le bureau » sous Sonoma et suivants le laisse en place, et c'est
TrayAppqui le ferme (2.5.5). Les clics sur l'icône du tray et sur son menu contextuel ne comptent pas comme des clics hors de la popup : un clic droit ouvre donc le menu au lieu de masquer la popup (2.5.5).
Et ensuite
- System tray — l'API d'icône tray sur laquelle repose
TrayApp. - DSL menu du tray — construire le menu du clic droit.
- Single instance — un re-launch depuis le
.appré-affiche la popup au lieu de démarrer un second process.
DSL menu du tray
Construisez le menu contextuel du tray depuis Kotlin avec des items, des cases à cocher, des sous-menus, des séparateurs et du contenu conditionnel.
Raccourcis globaux
Enregistrez des raccourcis clavier au niveau de l'OS qui se déclenchent même quand votre application n'a pas le focus.