11 KiB
Desktop Compose APIs Catalog
Complete reference for Compose Multiplatform Desktop-only APIs.
Window Management
application
Root entry point for desktop apps.
fun main() = application {
Window(onCloseRequest = ::exitApplication) {
Text("Hello Desktop")
}
}
Window
Creates a window.
Window(
onCloseRequest: () -> Unit,
state: WindowState = rememberWindowState(),
visible: Boolean = true,
title: String = "Untitled",
icon: Painter? = null,
undecorated: Boolean = false,
transparent: Boolean = false,
resizable: Boolean = true,
enabled: Boolean = true,
focusable: Boolean = true,
alwaysOnTop: Boolean = false,
onPreviewKeyEvent: ((KeyEvent) -> Boolean) = { false },
onKeyEvent: ((KeyEvent) -> Boolean) = { false },
content: @Composable FrameWindowScope.() -> Unit
)
Example:
val windowState = rememberWindowState(
width = 1200.dp,
height = 800.dp,
position = WindowPosition.Aligned(Alignment.Center)
)
Window(
onCloseRequest = ::exitApplication,
state = windowState,
title = "My App",
resizable = true
) {
// Content
}
rememberWindowState
Manages window size and position.
@Composable
fun rememberWindowState(
placement: WindowPlacement = WindowPlacement.Floating,
isMinimized: Boolean = false,
position: WindowPosition = WindowPosition.PlatformDefault,
width: Dp = Dp.Unspecified,
height: Dp = Dp.Unspecified
): WindowState
WindowPlacement:
Floating- Normal windowMaximized- FullscreenFullscreen- Fullscreen without decorations
WindowPosition:
PlatformDefault- OS decidesAligned(alignment)- Center, TopStart, etc.Absolute(x, y)- Fixed position in pixels
DialogWindow
Modal dialog.
DialogWindow(
onCloseRequest: () -> Unit,
state: DialogState = rememberDialogState(),
visible: Boolean = true,
title: String = "Dialog",
icon: Painter? = null,
undecorated: Boolean = false,
transparent: Boolean = false,
resizable: Boolean = true,
enabled: Boolean = true,
focusable: Boolean = true,
content: @Composable DialogWindowScope.() -> Unit
)
Example:
var showDialog by remember { mutableStateOf(false) }
if (showDialog) {
DialogWindow(
onCloseRequest = { showDialog = false },
title = "Confirm"
) {
Column(Modifier.padding(16.dp)) {
Text("Are you sure?")
Row {
Button(onClick = { showDialog = false }) {
Text("Cancel")
}
Button(onClick = { /* confirm */ }) {
Text("OK")
}
}
}
}
}
MenuBar
MenuBar
Native menu bar for windows.
@Composable
fun FrameWindowScope.MenuBar(
content: @Composable MenuBarScope.() -> Unit
)
Example:
Window(onCloseRequest = ::exitApplication) {
MenuBar {
Menu("File") {
Item("New", onClick = { /* ... */ })
Item("Open", onClick = { /* ... */ })
Separator()
Item("Quit", onClick = ::exitApplication)
}
Menu("Edit") {
Item("Copy", onClick = { /* ... */ })
Item("Paste", onClick = { /* ... */ })
}
}
}
Menu
Top-level menu.
@Composable
fun MenuBarScope.Menu(
text: String,
mnemonic: Char? = null,
enabled: Boolean = true,
content: @Composable MenuScope.() -> Unit
)
Item
Menu item.
@Composable
fun MenuScope.Item(
text: String,
onClick: () -> Unit,
shortcut: KeyShortcut? = null,
mnemonic: Char? = null,
enabled: Boolean = true,
icon: Painter? = null
)
With keyboard shortcut:
Item(
text = "Save",
onClick = { save() },
shortcut = KeyShortcut(Key.S, ctrl = true),
icon = painterResource("save.png")
)
Separator
Menu separator line.
@Composable
fun MenuScope.Separator()
CheckboxItem
Toggleable menu item.
@Composable
fun MenuScope.CheckboxItem(
text: String,
checked: Boolean,
onCheckedChange: (Boolean) -> Unit,
shortcut: KeyShortcut? = null,
mnemonic: Char? = null,
enabled: Boolean = true
)
Example:
var darkMode by remember { mutableStateOf(false) }
Menu("View") {
CheckboxItem(
text = "Dark Mode",
checked = darkMode,
onCheckedChange = { darkMode = it },
shortcut = KeyShortcut(Key.D, ctrl = true)
)
}
RadioButtonItem
Radio button menu item.
@Composable
fun MenuScope.RadioButtonItem(
text: String,
selected: Boolean,
onClick: () -> Unit,
shortcut: KeyShortcut? = null,
mnemonic: Char? = null,
enabled: Boolean = true
)
System Tray
Tray
System tray icon with menu.
@Composable
fun ApplicationScope.Tray(
icon: Painter,
state: TrayState = rememberTrayState(),
tooltip: String? = null,
onAction: () -> Unit = {},
menu: @Composable MenuScope.() -> Unit = {}
)
Example:
application {
var isVisible by remember { mutableStateOf(true) }
Tray(
icon = painterResource("tray-icon.png"),
tooltip = "My App",
onAction = { isVisible = true },
menu = {
Item("Show Window", onClick = { isVisible = true })
Separator()
Item("Quit", onClick = ::exitApplication)
}
)
if (isVisible) {
Window(
onCloseRequest = { isVisible = false },
title = "App"
) {
// Content
}
}
}
rememberTrayState
Manages tray state.
@Composable
fun rememberTrayState(): TrayState
Notifications
Notification (via Tray)
Show desktop notifications through tray.
val trayState = rememberTrayState()
Tray(
icon = painterResource("icon.png"),
state = trayState
)
// Send notification
LaunchedEffect(Unit) {
trayState.sendNotification(
Notification(
title = "Message",
message = "You have a new message",
type = Notification.Type.Info
)
)
}
Notification types:
Info- InformationWarning- WarningError- Error
Keyboard
KeyShortcut
Keyboard shortcut definition.
data class KeyShortcut(
val key: Key,
val ctrl: Boolean = false,
val meta: Boolean = false,
val alt: Boolean = false,
val shift: Boolean = false
)
Examples:
// Ctrl+S (Windows/Linux)
KeyShortcut(Key.S, ctrl = true)
// Cmd+S (macOS)
KeyShortcut(Key.S, meta = true)
// Ctrl+Shift+N
KeyShortcut(Key.N, ctrl = true, shift = true)
// Alt+F4
KeyShortcut(Key.F4, alt = true)
onPreviewKeyEvent / onKeyEvent
Window-level keyboard handlers.
Window(
onCloseRequest = ::exitApplication,
onPreviewKeyEvent = { event ->
if (event.key == Key.Escape && event.type == KeyEventType.KeyDown) {
// Handle Escape
true // Consume event
} else {
false // Propagate
}
}
) {
// Content
}
Mouse
PointerMoveFilter (Deprecated, use Modifier.pointerInput)
Box(
modifier = Modifier
.pointerInput(Unit) {
awaitPointerEventScope {
while (true) {
val event = awaitPointerEvent()
// Handle mouse events
}
}
}
)
Mouse cursor
Box(
modifier = Modifier.pointerHoverIcon(
icon = PointerIcon(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR))
)
) {
Text("Hover me")
}
Cursor types:
DEFAULT_CURSORHAND_CURSORTEXT_CURSORCROSSHAIR_CURSORWAIT_CURSORMOVE_CURSORE_RESIZE_CURSOR,W_RESIZE_CURSOR, etc.
Drag & Drop (Experimental)
onExternalDrag
Handle drag-and-drop from external sources.
Box(
modifier = Modifier
.size(200.dp)
.background(Color.LightGray)
.onExternalDrag(
onDragStart = { externalDragValue ->
println("Drag started")
},
onDrag = { externalDragValue ->
println("Dragging: ${externalDragValue.dragData}")
},
onDragExit = {
println("Drag exited")
},
onDrop = { externalDragValue ->
val dragData = externalDragValue.dragData
when (dragData) {
is DragData.FilesList -> {
println("Files dropped: ${dragData.readFiles()}")
}
is DragData.Text -> {
println("Text dropped: ${dragData.readText()}")
}
else -> {}
}
}
)
) {
Text("Drop files here", Modifier.align(Alignment.Center))
}
Resources
painterResource
Load images from resources.
val icon = painterResource("icon.png")
Icon(
painter = icon,
contentDescription = "App icon"
)
Resource location: src/jvmMain/resources/
Platform Integration
Desktop.getDesktop() (AWT)
Access system desktop features (not Compose API, but commonly used).
import java.awt.Desktop
import java.net.URI
// Open URL in browser
if (Desktop.isDesktopSupported()) {
Desktop.getDesktop().browse(URI("https://example.com"))
}
// Open file with default app
Desktop.getDesktop().open(File("/path/to/file.pdf"))
// Open email client
Desktop.getDesktop().mail(URI("mailto:user@example.com"))
FileDialog (AWT)
File picker dialogs.
import java.awt.FileDialog
import java.awt.Frame
// Open file
val fileDialog = FileDialog(Frame(), "Select file", FileDialog.LOAD)
fileDialog.isVisible = true
val selectedFile = fileDialog.file
val directory = fileDialog.directory
// Save file
val saveDialog = FileDialog(Frame(), "Save file", FileDialog.SAVE)
saveDialog.file = "document.txt"
saveDialog.isVisible = true
SwingPanel (Interop)
Embed Swing components in Compose.
import androidx.compose.ui.awt.SwingPanel
import javax.swing.JButton
SwingPanel(
factory = {
JButton("Swing Button").apply {
addActionListener {
println("Swing button clicked")
}
}
},
modifier = Modifier.size(200.dp, 50.dp)
)
ComposePanel (Reverse Interop)
Embed Compose in Swing.
import androidx.compose.ui.awt.ComposePanel
import javax.swing.JFrame
val frame = JFrame("Swing Frame")
val composePanel = ComposePanel()
composePanel.setContent {
Text("Compose in Swing")
}
frame.contentPane.add(composePanel)
frame.setSize(400, 300)
frame.isVisible = true
Version Requirements
- Kotlin: 2.0+
- Compose Multiplatform: 1.7.0+
- JVM Target: 11+ (recommend 21)
See also: