DEV Community

Rain9
Rain9

Posted on

Tauri(5)—— Tray Icon Implementation and Event Handling

This guide details the implementation of a tray icon in a Tauri application, including customizing the icon, creating menu items, and handling events. The steps also include dynamically switching the tray icon based on themes.

Dependencies

Add the required dependencies in src-tauri/Cargo.toml:

[dependencies]
tauri = { version = "2.0.6", features = ["tray-icon", "image-png"] }
Enter fullscreen mode Exit fullscreen mode

Tray Icon Setup

Function: enable_tray

fn enable_tray(app: &mut tauri::App) {
    use tauri::{
        image::Image,
        menu::{MenuBuilder, MenuItem},
        tray::TrayIconBuilder,
    };

    // Create menu items
    let quit_i = MenuItem::with_id(app, "quit", "Quit Coco", true, None::<&str>).unwrap();
    let settings_i = MenuItem::with_id(app, "settings", "Settings...", true, None::<&str>).unwrap();
    let open_i = MenuItem::with_id(app, "open", "Open Coco", true, None::<&str>).unwrap();
    let about_i = MenuItem::with_id(app, "about", "About Coco", true, None::<&str>).unwrap();
    let hide_i = MenuItem::with_id(app, "hide", "Hide Coco", true, None::<&str>).unwrap();

    // Build menu
    let menu = MenuBuilder::new(app)
        .item(&open_i)
        .separator()
        .item(&hide_i)
        .item(&about_i)
        .item(&settings_i)
        .separator()
        .item(&quit_i)
        .build()
        .unwrap();

    // Create tray icon
    let _tray = TrayIconBuilder::with_id("tray")
        .icon(Image::from_bytes(include_bytes!("../icons/light@2x.png")).expect("Failed to load icon"))
        .menu(&menu)
        .on_menu_event(|app, event| match event.id.as_ref() {
            "open" => handle_open_coco(app),
            "hide" => handle_hide_coco(app),
            "about" => { let _ = app.emit("open_settings", "about"); },
            "settings" => { let _ = app.emit("open_settings", ""); },
            "quit" => app.exit(0),
            _ => println!("Unhandled menu item: {:?}", event.id),
        })
        .build(app)
        .unwrap();
}
Enter fullscreen mode Exit fullscreen mode

Register Tray Function in run

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    let mut ctx = tauri::generate_context!();

    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![switch_tray_icon])
        .setup(|app| {
            init(app.app_handle());
            enable_tray(app); // Register tray events
            Ok(())
        })
        .run(ctx)
        .expect("Error while running Tauri application");
}
Enter fullscreen mode Exit fullscreen mode

Dynamically Switch Tray Icon

Rust Command: switch_tray_icon

#[tauri::command]
fn switch_tray_icon(app: tauri::AppHandle, is_dark_mode: bool) {
    println!("is_dark_mode: {}", is_dark_mode);

    const DARK_ICON_PATH: &[u8] = include_bytes!("../icons/dark@2x.png");
    const LIGHT_ICON_PATH: &[u8] = include_bytes!("../icons/light@2x.png");

    // Determine icon based on theme
    let icon_path: &[u8] = if is_dark_mode {
        DARK_ICON_PATH
    } else {
        LIGHT_ICON_PATH
    };

    // Fetch tray by ID
    if let Some(tray) = app.tray_by_id("tray") {
        if let Err(e) = tray.set_icon(Some(
            tauri::image::Image::from_bytes(icon_path)
                .unwrap_or_else(|e| panic!("Failed to load icon from bytes: {}", e)),
        )) {
            eprintln!("Failed to set tray icon: {}", e);
        }
    } else {
        eprintln!("Tray with ID 'tray' not found");
    }
}
Enter fullscreen mode Exit fullscreen mode

Frontend Invocation

import { invoke } from "@tauri-apps/api";

async function switchTrayIcon(value: "dark" | "light") {
    try {
        await invoke("switch_tray_icon", { isDarkMode: value === "dark" });
    } catch (err) {
        console.error("Failed to switch tray icon:", err);
    }
}
Enter fullscreen mode Exit fullscreen mode

Summary

  • Tray Icon Features: Create customizable tray icons with menus and handle user interactions.
  • Dynamic Theme Switching: Adjust tray icons dynamically to match light or dark themes.
  • Frontend Integration: Seamlessly invoke backend commands to manage tray features.

This setup ensures a polished user experience with a functional and dynamic tray in your Tauri application.

Open Source Project Sharing

Recently, I started developing a project based on Tauri, called Coco. The project is open source, and we’re actively improving it. Feel free to check it out and give it a star 🌟 if you find it helpful!

This is my first project with Tauri, and I’ve been learning and exploring along the way. I look forward to connecting with like-minded developers to exchange ideas and grow together!

Thank you so much for your support and attention!

Top comments (1)

Collapse
 
jonrandy profile image
Jon Randy 🎖️ • Edited

Where does the tray icon end up on Linux? Presumably it uses appindicator in some way?