Launcher (macOS)
Populate the Dock right-click menu of a macOS app with items, submenus, and separators, and handle clicks from Kotlin.
launcher-macos lets you populate the Dock right-click menu of a macOS app from Kotlin. You
define the menu entries — including submenus, separators, and disabled items — and receive a
callback on the Swing EDT when the user clicks one.
Add the dependency
dependencies {
implementation("dev.nucleusframework:nucleus.launcher-macos:2.0.7")
}Populate the Dock menu
Call MacOsDockMenu.setDockMenu(...) with the items to display, then set a listener to
handle clicks. Each item carries a numeric id that the listener receives.
import dev.nucleusframework.launcher.macos.DockMenuItem
import dev.nucleusframework.launcher.macos.DockMenuListener
import dev.nucleusframework.launcher.macos.MacOsDockMenu
MacOsDockMenu.setDockMenu(listOf(
DockMenuItem(id = 1, title = "New Window"),
DockMenuItem(id = 2, title = "Open File..."),
DockMenuItem.separator(id = 3),
DockMenuItem(
id = 4,
title = "Recent",
children = listOf(
DockMenuItem(id = 41, title = "project.kt"),
DockMenuItem(id = 42, title = "build.gradle.kts"),
),
),
DockMenuItem(id = 5, title = "Preferences"),
))
MacOsDockMenu.listener = DockMenuListener { id ->
when (id) {
1 -> openNewWindow()
2 -> openFile()
in 40..49 -> openRecent(id)
5 -> showPreferences()
}
}Call MacOsDockMenu.clearDockMenu() to remove the menu and restore the default Dock behavior.
All methods are no-ops off macOS and when the native library fails to load. Check
MacOsDockMenu.isAvailable before you build the menu.
How it works
The first call to setDockMenu(...) installs a method swizzle on the running
NSApplicationDelegate to intercept applicationDockMenu:. From then on, AppKit asks Nucleus
for the menu whenever the user right-clicks (or Ctrl-clicks) the Dock tile.
Click callbacks are delivered on the Swing EDT, so you can mutate Compose state directly from the listener without dispatching to another thread.
macOS strips images from Dock menu items. Only text, separators, submenus, and the enabled/disabled state are rendered — the Dock process owns the drawing, not your app.
For dragging files onto the Dock icon, AWT already exposes Desktop.setOpenFileHandler (and
Desktop.setOpenURIHandler for deep links). launcher-macos does not duplicate that.
API reference
MacOsDockMenu
The entry point. A single object with one global listener.
| Member | Notes |
|---|---|
isAvailable: Boolean | false off macOS or when the native library failed to load. |
listener: DockMenuListener? | One global listener; dispatch by id inside it. |
setDockMenu(items: List<DockMenuItem>) | Replaces the menu. Installs the swizzle on the first call. |
clearDockMenu() | Removes the menu; the OS default returns. |
DockMenuItem
A data class describing one entry.
| Property | Default | Notes |
|---|---|---|
id: Int | required, > 0 | Identifier passed to the listener. |
title: String | "" | Display text. |
enabled: Boolean | true | Whether the item is clickable. |
children: List<DockMenuItem> | emptyList() | A non-empty list makes the item a submenu. |
DockMenuItem.separator(id) builds a separator entry.
DockMenuListener
A fun interface with a single method, onItemClicked(itemId: Int), invoked on the Swing EDT.
Notes
- Test from a packaged build or
runDistributable. Thejavalauncher icon shown by./gradlew runuses no bundle identifier and will not display the menu meaningfully. - ProGuard / GraalVM: keep
dev.nucleusframework.launcher.macos.NativeMacOsDockMenuBridgereachable. The module ships the metadata.
What's next
- Taskbar progress — Dock badge, bounce, and progress state.
- Deep links — handle
Desktop.setOpenURIHandlerURL events. - Launcher (Windows) — taskbar jump lists and thumbnail toolbars.