1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
use crate::platform::{NativeString, TrayError};
use crate::MenuItem;
use std::rc::Rc;

pub struct Tray {
    pub(crate) guid: u128,
    pub(crate) title: NativeString,
    pub(crate) click: Option<Rc<dyn Fn()>>,
    pub(crate) menu: Option<Vec<MenuItem>>,
    #[cfg(target_os = "linux")]
    pub(crate) icon: Option<Vec<ksni::Icon>>,
}

impl Tray {
    pub fn new(guid: u128, title: &str) -> Self {
        Self {
            guid,
            title: title.into(),
            click: None,
            menu: None,
            #[cfg(target_os = "linux")]
            icon: None,
        }
    }

    pub fn set_click<F: 'static + Fn()>(&mut self, action: F) {
        self.click = Some(Rc::new(action));
    }

    pub fn set_menu(&mut self, menu: Vec<MenuItem>) {
        self.menu = Some(menu);
    }

    pub fn display(self) -> Result<(), TrayError> {
        crate::platform::display(self)
    }

    pub fn exit() {
        crate::platform::exit();
    }

    #[cfg(target_os = "linux")]
    pub fn set_icon(&mut self, bytes: &[u8]) {
        let cursor = std::io::Cursor::new(bytes);
        if let Ok(ico) = ico::IconDir::read(cursor) {
            let entries = ico.entries();
            let mut icons = Vec::with_capacity(entries.len());

            for enrty in entries {
                if let Ok(img) = enrty.decode() {
                    icons.push(ksni::Icon {
                        width: img.width() as i32,
                        height: img.height() as i32,
                        data: img
                            .rgba_data()
                            .chunks_exact(4)
                            .flat_map(|v| [v[3], v[0], v[1], v[2]])
                            .collect(),
                    });
                }
            }

            self.icon = Some(icons)
        }
    }
}