#tray #icons #menu #api-bindings #cross-platform

betrayer

Simple tray icons that integrate well with winit

2 unstable releases

0.2.0 Apr 20, 2024
0.1.0 Dec 29, 2023

#265 in GUI

Download history 1010/week @ 2024-07-22 1091/week @ 2024-07-29 1022/week @ 2024-08-05 851/week @ 2024-08-12 949/week @ 2024-08-19 1415/week @ 2024-08-26 1696/week @ 2024-09-02 1047/week @ 2024-09-09 835/week @ 2024-09-16 883/week @ 2024-09-23 931/week @ 2024-09-30 919/week @ 2024-10-07 977/week @ 2024-10-14 743/week @ 2024-10-21 788/week @ 2024-10-28 748/week @ 2024-11-04

3,403 downloads per month

MIT license

65KB
1.5K SLoC

Betrayer

Betrayer is a library for creating tray icons specifically designed to integrate well with winit and other existing libraries.

Example

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
enum Signal {
    Profile(u32),
    Open,
    Quit
}

fn main() -> Result<()> {
    let event_loop = EventLoopBuilder::with_user_event()
        .build()?;

    let mut selected = 0;
    
    let tray = TrayIconBuilder::new()
        .with_icon(Icon::from_rgba(vec![255u8; 32 * 32 * 4], 32, 32)?)
        .with_tooltip("Demo System Tray")
        .with_menu(build_menu(selected))
        // with `winit` feature:
        .build_event_loop(&event_loop, |e| Some(e))?;
        // without:
        //.build({
        //    let proxy = event_loop.create_proxy();
        //    move |s| {let _ = proxy.send_event(s); }
        //})?;

    event_loop.set_control_flow(ControlFlow::Wait);
    event_loop.run(|event, evtl| {
        match event {
            Event::UserEvent(event) => {
                println!("tray event: {:?}", event);
                if let TrayEvent::Menu(signal) = event {
                    match signal {
                        Signal::Profile(i) => {
                            if selected != i {
                                selected = i;
                                tray.set_tooltip(format!("Active Profile: {selected}"));
                                tray.set_menu(build_menu(selected));
                            }
                        },
                        Signal::Open => {}
                        Signal::Quit => evtl.exit()
                    }
                }
            }
            _ => {}
        }
    })?;
    Ok(())
}

fn build_menu(selected: u32) -> Menu<Signal> {
    Menu::new([
        MenuItem::menu("Profiles", (0..5)
            .map(|i| MenuItem::check_button(format!("Profile {}", i + 1), Signal::Profile(i),selected == i))),
        MenuItem::separator(),
        MenuItem::button("Open", Signal::Open),
        MenuItem::button("Quit", Signal::Quit)
    ])
}

Platform notes

On Windows and Mac this library uses the platform native toolkits and therefore needs a running eventloop on the same thread.

On Linux this library uses the org.kde.StatusNotifierItem DBus api and therefore requires a Desktop Environment that supports this api. Ubuntu possibly requires the libayatana-appindicator package.

This library will spawn its own thread to handle DBus communication so no extra eventloop is required.

Todo

General

  • Support for changing icons
  • Standalone mode that takes control of the main thread
  • More menu elements
  • More options for the tray

Windows

  • Recreate the tray when the taskbar restarts

Linux

  • Support creating the tray on existing async executors to avoid spawning the thread
  • Gracefully handle DBus config changes
  • Use OS managed temp file to avoid leaking icons on panics?

Mac

  • Add icon support
  • Generally more testing, I have no idea about Mac development and kinda freestyled this in a barely working VM.

Dependencies

~0–47MB
~767K SLoC