tray-icon

repository·dev·Indexed 18 days ago

https://github.com/tauri-apps/tray-icon

A Rust library for creating system tray icons for desktop applications across Windows, macOS, Linux, and FreeBSD. It provides functionality to build tray icons with tooltips and context menus, handle tray and menu events, and integrate with event loops like winit or tao. The library supports creating icons from RGBA data, Windows resource identifiers, or file paths (Windows only).

Tokens
3.8K
Snippets
16
Records
21
Agent score
63%

What's inside tray-icon

  1. Platform-specific threading requirements

    dev

    The placement of the tray icon creation and the requirement for an event loop depend on the platform:

    • Windows & Linux/FreeBSD: An event loop (Win32 on Windows, GTK on Linux/FreeBSD) must be running on the thread. You must create the tray icon on the same thread as the event loop. It does not have to be the main thread.
    • macOS: An event loop must be running on the main thread, so you must also create the tray icon on the main thread.
  2. Install dependencies for Linux

    dev

    To use tray-icon on Linux, you must install gtk3 and libappindicator (or libayatana-appindicator). If you want to use predefined menu items like Copy, Cut, Paste, and SelectAll, you must also install libxdo.

    Use the following commands based on your distribution:

    #### Arch Linux / Manjaro:
    ```sh
    pacman -S gtk3 xdotool libappindicator-gtk3 #or libayatana-appindicator

    Debian / Ubuntu:

    sudo apt install libgtk-3-dev libxdo-dev libappindicator3-dev #or libayatana-appindicator3-dev
  3. Integrate tray events with winit or tao event loops

    dev

    When using winit or tao, you should not poll the receivers manually. Instead, use TrayIconEvent::set_event_handler and MenuEvent::set_event_handler to forward events to your event loop via an EventLoopProxy. This ensures the event loop is awakened immediately when a tray or menu event occurs.

    enum UserEvent {
      TrayIconEvent(tray_icon::TrayIconEvent)
      MenuEvent(tray_icon::menu::MenuEvent)
    }
    
    let event_loop = EventLoop::<UserEvent>::with_user_event().build().unwrap();
    let proxy = event_loop.create_proxy();
    
    // Forward tray events
    tray_icon::TrayIconEvent::set_event_handler(Some(move |event| {
        proxy.send_event(UserEvent::TrayIconEvent(event));
    }));
    
    // Forward menu events
    tray_icon::menu::MenuEvent::set_event_handler(Some(move |event| {
        proxy.send_event(UserEvent::MenuEvent(event));
    }));
  4. Configure Cargo features

    dev

    The following Cargo features are available to extend tray-icon functionality:

    • common-controls-v6: Uses the TaskDialogIndirect API from ComCtl32.dll v6 on Windows for the predefined About menu item dialog.
    • libxdo: Enables linking to libxdo to support predefined Copy, Cut, Paste, and SelectAll menu items.
    • serde: Enables de/serializing derives.
  5. Create a tray icon with TrayIconBuilder

    dev

    Use TrayIconBuilder to configure and add a new tray icon to the system. You can set the icon, tooltip, and an optional context menu.

    Platform Notes:

    • Windows/Linux: An event loop (Win32 or GTK) must be running on the thread where the icon is created. The icon must be created on the same thread as the event loop.
    • macOS: The icon must be created on the main thread, and the event loop must already be running (e.g., in winit, use StartCause::Init).
    • Linux: If an icon is not visible, try setting an empty Menu.
    use tray_icon::{TrayIconBuilder, Icon};
    
    // Assuming icon is already created via Icon::from_rgba
    let tray_icon = TrayIconBuilder::new()
        .with_tooltip("system-tray - tray icon library!")
        .with_icon(icon)
        .build()
        .unwrap();
  6. Process tray icon events

    dev

    You can listen for tray icon interactions (like clicks, enters, or leaves) using TrayIconEvent::receiver(). This returns a TrayIconEventReceiver (a crossbeam channel receiver).

    Note: If you use TrayIconEvent::set_event_handler, events will be sent to that handler instead of the channel returned by receiver().

    use tray_icon::TrayIconEvent;
    
    if let Ok(event) = TrayIconEvent::receiver().try_recv() {
        println!("{:?}", event);
    }
  7. Forward tray events to an event loop (winit/tao)

    dev

    When using event loops like winit or tao, you should use TrayIconEvent::set_event_handler to forward events to the loop via an EventLoopProxy. This ensures the event loop is awakened when a tray event occurs.

    # use winit::event_loop::EventLoop;
    enum UserEvent {
      TrayIconEvent(tray_icon::TrayIconEvent),
      MenuEvent(tray_icon::menu::MenuEvent)
    }
    
    let event_loop = EventLoop::<UserEvent>::with_user_event().build().unwrap();
    let proxy = event_loop.create_proxy();
    
    tray_icon::TrayIconEvent::set_event_handler(Some(move |event| {
        proxy.send_event(UserEvent::TrayIconEvent(event));
    }));
    
    tray_icon::menu::MenuEvent::set_event_handler(Some(move |event| {
        proxy.send_event(UserEvent::MenuEvent(event));
    }));
  8. Install Linux dependencies for tray-icon

    dev

    On Linux, tray-icon requires gtk, libxdo, and either libappindicator or libayatana-appindicator to function correctly.

    Arch Linux / Manjaro:

    pacman -S gtk3 xdotool libappindicator-gtk3 #or libayatana-appindicator

    Debian / Ubuntu:

    sudo apt install libgtk-3-dev libxdo-dev libappindicator3-dev #or libayatana-appindicator3-dev
  9. Create a tray icon without a menu

    dev

    Use TrayIconBuilder to create a basic tray icon. You can specify a tooltip and an icon.

    use tray_icon::TrayIconBuilder;
    
    let tray_icon = TrayIconBuilder::new()
        .with_tooltip("system-tray - tray icon library!")
        .with_icon(icon)
        .build()
        .unwrap();
  10. Create a tray icon with a menu

    dev

    To add a context menu to your tray icon, create a Menu instance and pass it to the TrayIconBuilder using .with_menu(Box::new(tray_menu)).

    use tray_icon::{TrayIconBuilder, menu::Menu};
    
    let tray_menu = Menu::new();
    let tray_icon = TrayIconBuilder::new()
        .with_menu(Box::new(tray_menu))
        .with_tooltip("system-tray - tray icon library!")
        .with_icon(icon)
        .build()
        .unwrap();
  11. Process tray and menu events

    dev

    Events for tray icon clicks and menu interactions can be retrieved using receivers.

    • Use TrayIconEvent::receiver() to listen for tray icon click events.
    • Use MenuEvent::receiver() to listen for menu item interaction events.
    use tray_icon::{TrayIconEvent, menu::{MenuEvent}};
    
    // Listen for tray icon events
    if let Ok(event) = TrayIconEvent::receiver().try_recv() {
        println!("tray event: {:?}", event);
    }
    
    // Listen for menu events
    if let Ok(event) = MenuEvent::receiver().try_recv() {
        println!("menu event: {:?}", event);
    }