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.
 
 
 
 

9.1 KiB

Menu, tray, notifications

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
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.v1.MenuItem
import androidx.compose.ui.window.v1.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
}

Tray

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
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
}

Notifier

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.v1.KeyStroke
import androidx.compose.ui.window.v1.MenuItem
import androidx.compose.ui.window.v1.Menu
import androidx.compose.ui.window.v1.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)
        }
    }
}

Application MenuBar

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.v1.KeyStroke
import androidx.compose.ui.window.v1.MenuItem
import androidx.compose.ui.window.v1.Menu
import androidx.compose.ui.window.v1.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)
        }
    }
}

Window MenuBar