SkillAgentSearch skills...

ComposeNativeTray

ComposeTray is a Kotlin library that provides a simple way to create system tray applications with native support for Mac, Linux and Windows. This library allows you to add a system tray icon, tooltip, and menu with various options in a Kotlin DSL-style syntax.

Install / Use

/learn @kdroidFilter/ComposeNativeTray
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

🛠️ Compose Native Tray

<p align="center"> <img src="screenshots/logo.png" alt="logo"> </p> <p align="center"> <a href="https://central.sonatype.com/artifact/io.github.kdroidfilter/composenativetray"><img src="https://img.shields.io/maven-central/v/io.github.kdroidfilter/composenativetray" alt="Maven Central"></a> <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a> <a href="https://github.com/kdroidFilter/ComposeNativeTray"><img src="https://img.shields.io/badge/Platform-Linux%20%7C%20Windows%20%7C%20macOS-lightgrey.svg" alt="Platform"></a> <a href="https://github.com/kdroidFilter/ComposeNativeTray/commits/main"><img src="https://img.shields.io/github/last-commit/kdroidFilter/ComposeNativeTray" alt="Last Commit"></a> <a href="https://kdroidfilter.github.io/ComposeNativeTray/"><img src="https://img.shields.io/badge/docs-Dokka-blue.svg" alt="Documentation"></a> <a href="https://github.com/kdroidFilter/ComposeNativeTray/issues"><img src="https://img.shields.io/badge/contributions-welcome-brightgreen.svg" alt="Contributions Welcome"></a> <a href="https://github.com/kdroidFilter/ComposeNativeTray/actions"><img src="https://img.shields.io/badge/build-passing-brightgreen.svg" alt="Build Passing"></a> </p>

📖 Introduction

Compose Native Tray is a modern Kotlin library for creating applications with system tray icons, offering native support for Linux, Windows, and macOS. It uses an intuitive Kotlin DSL syntax and fixes issues with the standard Compose for Desktop solution.

✨ Features

  • Cross-platform support for Linux, Windows, and macOS.
  • DSL-style syntax to define tray menus with ease.
  • Supports standard items, submenus, dividers, and checkable items.
  • Ability to enable/disable menu items dynamically.
  • Corrects issues with the Compose for Desktop tray, particularly HDPI support on Windows and Linux.
  • Improves the appearance of the tray on Linux, which previously resembled Windows 95.
  • Adds support for checkable items, dividers, and submenus, including nested submenus.
  • Supports primary action for Windows, macOS, and Linux.
    • On Windows and macOS, the primary action is triggered by a left-click on the tray icon.
    • On Linux, on GNOME the primary action is triggered by a double left-click on the tray icon, while on the majority of other environments, primarily KDE Plasma, it is triggered by a single left-click, similar to Windows and macOS.
  • Single Instance Management: Ensures that only one instance of the application can run at a time and allows restoring focus to the running instance when another instance is attempted.
  • Tray Position Detection: Allows determining the position of the system tray, which helps in positioning related windows appropriately.
  • Compose Recomposition Support: The tray supports Compose recomposition, making it possible to dynamically show or hide the tray icon, for example:
<p align="center"> <img src="screenshots/demo.gif" alt="demo"> </p>

📑 Table of Contents

🎯 Why Compose Native Tray?

This library was created to solve several limitations of the standard Compose for Desktop solution:

  • Improved HDPI support on Windows and Linux
  • Modern appearance on Linux (no more Windows 95 look!)
  • Extended features: checkable items, nested submenus, separators
  • Native primary action: left-click on Windows/macOS, single-click (KDE) or double-click (GNOME) on Linux
  • Full Compose recomposition support: fully reactive icon and menu, allowing dynamic updates of items, their states, and visibility

📸 Preview

<table> <tr> <td><img src="screenshots/windows.png" alt="Windows" /><br /><center>Windows</center></td> <td><img src="screenshots/mac.png" alt="macOS" /><br /><center>macOS</center></td> </tr> <tr> <td><img src="screenshots/gnome.png" alt="Ubuntu GNOME" /><br /><center>Ubuntu GNOME</center></td> <td><img src="screenshots/kde.png" alt="Ubuntu KDE" /><br /><center>Ubuntu KDE</center></td> </tr> </table>

⚡ Installation

Add the dependency to your build.gradle.kts:

dependencies {
  implementation("io.github.kdroidfilter:composenativetray:<version>")
}

🚀 Quick Start

Minimal example to create a system tray icon with menu:

application {
  Tray(
    icon = Icons.Default.Favorite,
    tooltip = "My Application"
  ) {
    Item(label = "Settings") {
      println("Settings opened")
    }
    
    Divider()
    
    Item(label = "Exit") {
      exitProcess(0)
    }
  }
}

💡 Recommendation: It is highly recommended to check out the demo examples in the project's demo directory. These examples showcase various implementation patterns and features that will help you better understand how to use the library effectively.

Notable demos:

  • DemoWithDrawableResources.kt – shows using DrawableResource directly for Tray and menu icons
  • PainterResourceWorkaroundDemo.kt – demonstrates the painterResource variable workaround
  • DemoWithoutContextMenu.kt – minimalist tray with primary action only

📚 Usage Guide

🎨 Creating the System Tray Icon

New: Using a DrawableResource directly

Tray(
  icon = Res.drawable.myIcon,  // org.jetbrains.compose.resources.DrawableResource
  tooltip = "My Application"
) { /* menu */ }

Requires compose.components.resources in your project. In this library it's already included; in your app add: implementation(compose.components.resources)

Option 1: Using an ImageVector

Tray(
  icon = Icons.Default.Favorite,
  tint = null,  // Optional: if null, the tint automatically adapts (white in dark mode, black in light mode) according to the isMenuBarInDarkMode() API
  tooltip = "My Application"
) { /* menu */ }

Option 2: Using a Painter

Tray(
  icon = painterResource(Res.drawable.myIcon),
  tooltip = "My Application"
) { /* menu */ }

Option 3: Using a Custom Composable

Tray(
  iconContent = {
    Canvas(modifier = Modifier.fillMaxSize()) { // Important to use fillMaxSize()!
      // A simple red circle as an icon
      drawCircle(
        color = Color.Red,
        radius = size.minDimension / 2,
        center = center
      )
    }
  },
  tooltip = "My Application"
) { /* menu */ }

⚠️ Important: Always use Modifier.fillMaxSize() with iconContent for proper icon rendering.

Option 4: Platform-Specific Icons

This approach allows respecting the design conventions of each platform:

  • Windows: Traditionally uses colored icons in the system tray
  • macOS/Linux: Prefer monochrome icons that automatically adapt to the theme
val windowsIcon = painterResource(Res.drawable.myIcon)
val macLinuxIcon = Icons.Default.Favorite

Tray(
  windowsIcon = windowsIcon,      // Windows: full colored icon
  macLinuxIcon = macLinuxIcon,    // macOS/Linux: adaptive icon
  tooltip = "My Application"
) { /* menu */ }

💡 Note: If no tint is specified, ImageVectors are automatically tinted white (dark mode) or black (light mode) based on the theme.

🖱️ Primary Action

Define an action for clicking on the icon. The behavior varies by platform:

  • Windows/macOS: Left-click on the icon (native implementation for macOS)
  • Linux: Single-click on KDE or double-click on GNOME (implementation via DBus)
Tray(
  icon = Icons.Default.Favorite,
  tooltip = "My Application",
  primaryAction = {
    println("Icon clicked!")
    // Open a window, display a menu, etc.
  }
) { /* menu */ }

📋 Building the Menu

Important note: It's not mandatory to create a context menu. You can use only an icon in the tray with a primary action (left-click) to restore your application, as shown in the DemoWithoutContextMenu.kt example. This minimalist approach is perfect for simple applications that only need a restore function.

The menu uses an intuitive DSL syntax with several types of elements:

Tray(/* configuration */) {
  // Simple item with icon
  Item(label = "Open", icon = Icons.Default.OpenInNew) {
    // Click action
  }
  
  // Item with custom icon via iconContent
  Item(
    label = "Custom",
    iconContent = {
      Icon(
        Icons.Default.Star,
        contentDescription = null,
        tint = Color.Yellow,
        modifier = Modifier.fillMaxSize() // Important!
      )
    }
  ) { }
  
  // Checkable item
  CheckableItem(
    label = "Dark Mode",
    icon = Icons.Default.DarkMode,
    checked = isDarkMode,
    onCheckedC
View on GitHub
GitHub Stars350
CategoryCustomer
Updated2d ago
Forks12

Languages

Kotlin

Security Score

95/100

Audited on Mar 22, 2026

No findings