9.1 KiB
Tray and menu notification
What is covered
In this tutorial we'll show you how to work with the system tray, create an application menu bar and a window-specific menu bar, and send system notifications using Compose for Desktop.
Tray
You can add an application icon to the system tray. You can also send notifications to the user using the system tray. There are 3 types of notification:
- notify - simple notification
- warn - warning notification
- error - error notification
import androidx.compose.desktop.AppManager
import androidx.compose.desktop.Window
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.Text
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.window.MenuItem
import androidx.compose.ui.window.Tray
import java.awt.Color
import java.awt.image.BufferedImage
fun main() {
val count = mutableStateOf(0)
Window(
icon = getMyAppIcon()
) {
DisposableEffect(Unit) {
val tray = Tray().apply {
icon(getTrayIcon())
menu(
MenuItem(
name = "Increment value",
onClick = {
count.value++
}
),
MenuItem(
name = "Send notification",
onClick = {
notify("Notification", "Message from MyApp!")
}
),
MenuItem(
name = "Exit",
onClick = {
AppManager.exit()
}
)
)
}
onDispose {
tray.remove()
}
}
// content
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(text = "Value: ${count.value}")
}
}
}
fun getMyAppIcon(): BufferedImage {
val size = 256
val image = BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB)
val graphics = image.createGraphics()
graphics.setColor(Color.green)
graphics.fillOval(size / 4, 0, size / 2, size)
graphics.setColor(Color.blue)
graphics.fillOval(0, size / 4, size, size / 2)
graphics.setColor(Color.red)
graphics.fillOval(size / 4, size / 4, size / 2, size / 2)
graphics.dispose()
return image
}
fun getTrayIcon(): BufferedImage {
val size = 256
val image = BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB)
val graphics = image.createGraphics()
graphics.setColor(Color.orange)
graphics.fillOval(0, 0, size, size)
graphics.dispose()
return image
}
Notifier
You can send system notifications with Notifier without using the system tray. Notifier also has 3 types of notification:
- notify - simple notification
- warn - warning notification
- error - error notification
import androidx.compose.desktop.Window
import androidx.compose.foundation.layout.Column
import androidx.compose.material.Text
import androidx.compose.material.Button
import androidx.compose.ui.window.Notifier
import java.awt.Color
import java.awt.image.BufferedImage
fun main() {
val message = "Some message!"
val notifier = Notifier()
Window(
icon = getMyAppIcon()
) {
Column {
Button(onClick = { notifier.notify("Notification.", message) }) {
Text(text = "Notify")
}
Button(onClick = { notifier.warn("Warning.", message) }) {
Text(text = "Warning")
}
Button(onClick = { notifier.error("Error.", message) }) {
Text(text = "Error")
}
}
}
}
fun getMyAppIcon() : BufferedImage {
val size = 256
val image = BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB)
val graphics = image.createGraphics()
graphics.setColor(Color.green)
graphics.fillOval(size / 4, 0, size / 2, size)
graphics.setColor(Color.blue)
graphics.fillOval(0, size / 4, size, size / 2)
graphics.setColor(Color.red)
graphics.fillOval(size / 4, size / 4, size / 2, size / 2)
graphics.dispose()
return image
}
MenuBar
MenuBar is used to create and customize the common context menu of the application or a particular window. To create a common context menu for all the application windows, you need to configure the AppManager.
import androidx.compose.desktop.AppManager
import androidx.compose.desktop.Window
import androidx.compose.material.Text
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.Modifier
import androidx.compose.ui.window.KeyStroke
import androidx.compose.ui.window.MenuItem
import androidx.compose.ui.window.Menu
import androidx.compose.ui.window.MenuBar
fun main() {
// To use Apple global menu.
System.setProperty("apple.laf.useScreenMenuBar", "true")
val action = mutableStateOf("Last action: None")
AppManager.setMenu(
MenuBar(
Menu(
name = "Actions",
MenuItem(
name = "About",
onClick = { action.value = "Last action: About (Command + I)" },
shortcut = KeyStroke(Key.I)
),
MenuItem(
name = "Exit",
onClick = { AppManager.exit() },
shortcut = KeyStroke(Key.X)
)
),
Menu(
name = "File",
MenuItem(
name = "Copy",
onClick = { action.value = "Last action: Copy (Command + C)" },
shortcut = KeyStroke(Key.C)
),
MenuItem(
name = "Paste",
onClick = { action.value = "Last action: Paste (Command + V)" },
shortcut = KeyStroke(Key.V)
)
)
)
)
Window {
// content
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(text = action.value)
}
}
}
You can create a MenuBar for a specific window, and have the other windows use the defined MenuBar.
import androidx.compose.desktop.AppManager
import androidx.compose.desktop.Window
import androidx.compose.material.Text
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.Button
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.Modifier
import androidx.compose.ui.window.KeyStroke
import androidx.compose.ui.window.MenuItem
import androidx.compose.ui.window.Menu
import androidx.compose.ui.window.MenuBar
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
fun main() {
// To use Apple global menu.
System.setProperty("apple.laf.useScreenMenuBar", "true")
val action = mutableStateOf("Last action: None")
Window(
menuBar = MenuBar(
Menu(
name = "Actions",
MenuItem(
name = "About",
onClick = { action.value = "Last action: About (Command + I)" },
shortcut = KeyStroke(Key.I)
),
MenuItem(
name = "Exit",
onClick = { AppManager.exit() },
shortcut = KeyStroke(Key.X)
)
),
Menu(
name = "File",
MenuItem(
name = "Copy",
onClick = { action.value = "Last action: Copy (Command + C)" },
shortcut = KeyStroke(Key.C)
),
MenuItem(
name = "Paste",
onClick = { action.value = "Last action: Paste (Command + V)" },
shortcut = KeyStroke(Key.V)
)
)
)
) {
// content
Button(
onClick = {
Window(
title = "Another window",
size = IntSize(350, 200),
location = IntOffset(100, 100),
centered = false
) {
}
}
) {
Text(text = "New window")
}
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(text = action.value)
}
}
}