You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

308 lines
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:
1. notify - simple notification
2. warn - warning notification
3. error - error notification
```kotlin
import androidx.compose.desktop.AppManager
import androidx.compose.desktop.Window
4 years ago
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.Text
import androidx.compose.runtime.DisposableEffect
4 years ago
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
4 years ago
import androidx.compose.ui.window.MenuItem
import androidx.compose.ui.window.Tray
4 years ago
import java.awt.Color
import java.awt.image.BufferedImage
fun main() {
4 years ago
val count = mutableStateOf(0)
4 years ago
Window(
icon = getMyAppIcon()
) {
DisposableEffect(Unit) {
val tray = Tray().apply {
4 years ago
icon(getTrayIcon())
menu(
4 years ago
MenuItem(
name = "Increment value",
onClick = {
4 years ago
count.value++
}
),
4 years ago
MenuItem(
name = "Send notification",
onClick = {
4 years ago
notify("Notification", "Message from MyApp!")
}
),
4 years ago
MenuItem(
name = "Exit",
onClick = {
AppManager.exit()
}
)
)
}
onDispose {
tray.remove()
}
}
4 years ago
// content
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
4 years ago
) {
Text(text = "Value: ${count.value}")
}
}
}
4 years ago
fun getMyAppIcon(): BufferedImage {
4 years ago
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 {
4 years ago
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
}
```
4 years ago
![Tray](tray.gif)
## Notifier
You can send system notifications with Notifier without using the system tray.
Notifier also has 3 types of notification:
1. notify - simple notification
2. warn - warning notification
3. error - error notification
```kotlin
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
4 years ago
import java.awt.Color
import java.awt.image.BufferedImage
fun main() {
val message = "Some message!"
4 years ago
val notifier = Notifier()
4 years ago
Window(
icon = getMyAppIcon()
) {
Column {
4 years ago
Button(onClick = { notifier.notify("Notification.", message) }) {
Text(text = "Notify")
}
4 years ago
Button(onClick = { notifier.warn("Warning.", message) }) {
Text(text = "Warning")
}
4 years ago
Button(onClick = { notifier.error("Error.", message) }) {
Text(text = "Error")
}
}
}
}
4 years ago
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
}
```
4 years ago
![Notifier](notifier.gif)
## 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.
```kotlin
import androidx.compose.desktop.AppManager
import androidx.compose.desktop.Window
import androidx.compose.material.Text
4 years ago
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")
4 years ago
val action = mutableStateOf("Last action: None")
AppManager.setMenu(
MenuBar(
Menu(
name = "Actions",
4 years ago
MenuItem(
name = "About",
4 years ago
onClick = { action.value = "Last action: About (Command + I)" },
shortcut = KeyStroke(Key.I)
),
4 years ago
MenuItem(
name = "Exit",
onClick = { AppManager.exit() },
4 years ago
shortcut = KeyStroke(Key.X)
)
),
Menu(
name = "File",
4 years ago
MenuItem(
name = "Copy",
4 years ago
onClick = { action.value = "Last action: Copy (Command + C)" },
shortcut = KeyStroke(Key.C)
),
4 years ago
MenuItem(
name = "Paste",
4 years ago
onClick = { action.value = "Last action: Paste (Command + V)" },
shortcut = KeyStroke(Key.V)
)
)
)
)
Window {
// content
4 years ago
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
4 years ago
) {
Text(text = action.value)
}
}
}
```
4 years ago
![Application MenuBar](app_menubar.gif)
You can create a MenuBar for a specific window, and have the other windows use the defined MenuBar.
```kotlin
import androidx.compose.desktop.AppManager
import androidx.compose.desktop.Window
import androidx.compose.material.Text
4 years ago
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
4 years ago
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")
4 years ago
val action = mutableStateOf("Last action: None")
Window(
menuBar = MenuBar(
Menu(
name = "Actions",
4 years ago
MenuItem(
name = "About",
4 years ago
onClick = { action.value = "Last action: About (Command + I)" },
shortcut = KeyStroke(Key.I)
),
4 years ago
MenuItem(
name = "Exit",
onClick = { AppManager.exit() },
4 years ago
shortcut = KeyStroke(Key.X)
)
),
Menu(
name = "File",
4 years ago
MenuItem(
name = "Copy",
4 years ago
onClick = { action.value = "Last action: Copy (Command + C)" },
shortcut = KeyStroke(Key.C)
),
4 years ago
MenuItem(
name = "Paste",
4 years ago
onClick = { action.value = "Last action: Paste (Command + V)" },
shortcut = KeyStroke(Key.V)
)
)
)
) {
// content
4 years ago
Button(
onClick = {
Window(
title = "Another window",
size = IntSize(350, 200),
location = IntOffset(100, 100),
centered = false
) {
4 years ago
}
}
) {
Text(text = "New window")
}
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
4 years ago
) {
Text(text = action.value)
}
}
}
```
4 years ago
![Window MenuBar](window_menubar.gif)