Skip to content

Latest commit

 

History

History
executable file
·
311 lines (279 loc) · 9.31 KB

File metadata and controls

executable file
·
311 lines (279 loc) · 9.31 KB

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.ExperimentalComposeUiApi
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

@OptIn(ExperimentalComposeUiApi::class)
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.ExperimentalComposeUiApi
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

@OptIn(ExperimentalComposeUiApi::class)
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