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