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.
177 lines
5.8 KiB
177 lines
5.8 KiB
3 years ago
|
# Menu, tray, notifications (new Composable API, experimental)
|
||
|
|
||
|
## What is covered
|
||
|
|
||
|
In this tutorial we'll show you how to work with the system tray, send system notifications, and create a menu bar 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.foundation.layout.Box
|
||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||
|
import androidx.compose.material.Text
|
||
|
import androidx.compose.runtime.mutableStateOf
|
||
|
import androidx.compose.runtime.remember
|
||
|
import androidx.compose.runtime.getValue
|
||
|
import androidx.compose.runtime.setValue
|
||
|
import androidx.compose.ui.Alignment
|
||
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
||
|
import androidx.compose.ui.Modifier
|
||
|
import androidx.compose.ui.window.Notification
|
||
|
import androidx.compose.ui.window.Tray
|
||
|
import androidx.compose.ui.window.Window
|
||
|
import androidx.compose.ui.window.application
|
||
|
import androidx.compose.ui.window.rememberTrayState
|
||
|
import java.awt.Color
|
||
|
import java.awt.image.BufferedImage
|
||
|
|
||
|
@OptIn(ExperimentalComposeUiApi::class)
|
||
|
fun main() = application {
|
||
|
var count by remember { mutableStateOf(0) }
|
||
|
var isOpen by remember { mutableStateOf(true) }
|
||
|
|
||
|
if (isOpen) {
|
||
|
Window(
|
||
|
icon = remember { getMyAppIcon() }
|
||
|
) {
|
||
|
val trayState = rememberTrayState()
|
||
|
val notification = Notification("Notification", "Message from MyApp!")
|
||
|
Tray(
|
||
|
state = trayState,
|
||
|
icon = remember { getTrayIcon() },
|
||
|
menu = {
|
||
|
Item(
|
||
|
"Increment value",
|
||
|
onClick = {
|
||
|
count++
|
||
|
}
|
||
|
)
|
||
|
Item(
|
||
|
"Send notification",
|
||
|
onClick = {
|
||
|
trayState.sendNotification(notification)
|
||
|
}
|
||
|
)
|
||
|
Item(
|
||
|
"Exit",
|
||
|
onClick = {
|
||
|
isOpen = false
|
||
|
}
|
||
|
)
|
||
|
}
|
||
|
)
|
||
|
|
||
|
// content
|
||
|
Box(
|
||
|
modifier = Modifier.fillMaxSize(),
|
||
|
contentAlignment = Alignment.Center
|
||
|
) {
|
||
|
Text(text = "Value: ${count}")
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fun getMyAppIcon(): BufferedImage {
|
||
|
val size = 256
|
||
|
val image = BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB)
|
||
|
val graphics = image.createGraphics()
|
||
|
graphics.color = Color.green
|
||
|
graphics.fillOval(size / 4, 0, size / 2, size)
|
||
|
graphics.color = Color.blue
|
||
|
graphics.fillOval(0, size / 4, size, size / 2)
|
||
|
graphics.color = 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.color = Color.orange
|
||
|
graphics.fillOval(0, 0, size, size)
|
||
|
graphics.dispose()
|
||
|
return image
|
||
|
}
|
||
|
```
|
||
|
|
||
|
![](tray.gif)
|
||
|
|
||
|
## MenuBar
|
||
|
|
||
|
MenuBar is used to create and customize the menu bar for a particular window.
|
||
|
|
||
|
```kotlin
|
||
|
import androidx.compose.foundation.layout.Box
|
||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||
|
import androidx.compose.material.Text
|
||
|
import androidx.compose.runtime.getValue
|
||
|
import androidx.compose.runtime.mutableStateOf
|
||
|
import androidx.compose.runtime.remember
|
||
|
import androidx.compose.runtime.setValue
|
||
|
import androidx.compose.ui.Alignment
|
||
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
||
|
import androidx.compose.ui.Modifier
|
||
|
import androidx.compose.ui.window.MenuBar
|
||
|
import androidx.compose.ui.window.Window
|
||
|
import androidx.compose.ui.window.application
|
||
|
|
||
|
@OptIn(ExperimentalComposeUiApi::class)
|
||
|
fun main() {
|
||
|
// Currently we use Swing's menu under the hood, so we need to set this property to change look and feel of the menu on Windows/Linux
|
||
|
System.setProperty("skiko.rendering.laf.global", "true")
|
||
|
|
||
|
application {
|
||
|
var action by remember { mutableStateOf("Last action: None") }
|
||
|
var isOpen by remember { mutableStateOf(true) }
|
||
|
|
||
|
if (isOpen) {
|
||
|
var isSubmenuShowing by remember { mutableStateOf(false) }
|
||
|
|
||
|
Window {
|
||
|
MenuBar {
|
||
|
Menu("Actions") {
|
||
|
Item(
|
||
|
if (isSubmenuShowing) "Hide advanced settings" else "Show advanced settings",
|
||
|
onClick = {
|
||
|
isSubmenuShowing = !isSubmenuShowing
|
||
|
}
|
||
|
)
|
||
|
if (isSubmenuShowing) {
|
||
|
Menu("Settings") {
|
||
|
Item("Setting 1", onClick = { action = "Last action: Setting 1" })
|
||
|
Item("Setting 2", onClick = { action = "Last action: Setting 2" })
|
||
|
}
|
||
|
}
|
||
|
Separator()
|
||
|
Item("About", onClick = { action = "Last action: About" })
|
||
|
Item("Exit", onClick = { isOpen = false })
|
||
|
}
|
||
|
Menu("File") {
|
||
|
Item("Copy", onClick = { action = "Last action: Copy" })
|
||
|
Item("Paste", onClick = { action = "Last action: Paste" },)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Box(
|
||
|
modifier = Modifier.fillMaxSize(),
|
||
|
contentAlignment = Alignment.Center
|
||
|
) {
|
||
|
Text(text = action)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
![](window_menubar.gif)
|