system_tray/
data.rs

1use crate::{
2    item::StatusNotifierItem,
3    menu::{MenuDiff, MenuItem, MenuItemUpdate, TrayMenu},
4};
5use std::sync::{Arc, Mutex};
6
7#[cfg(feature = "data")]
8use {crate::client::UpdateEvent, tracing::error};
9
10#[cfg(feature = "data")]
11pub type BaseMap = std::collections::HashMap<String, (StatusNotifierItem, Option<TrayMenu>)>;
12
13#[cfg(not(feature = "data"))]
14type BaseMap = std::collections::HashSet<String>;
15
16#[derive(Debug, Clone)]
17pub(crate) struct TrayItemMap {
18    inner: Arc<Mutex<BaseMap>>,
19}
20
21impl TrayItemMap {
22    pub(crate) fn new() -> Self {
23        Self {
24            inner: Arc::new(Mutex::new(BaseMap::default())),
25        }
26    }
27
28    #[cfg(feature = "data")]
29    pub(crate) fn get_map(&self) -> Arc<Mutex<BaseMap>> {
30        self.inner.clone()
31    }
32
33    pub(crate) fn new_item(&self, dest: String, item: &StatusNotifierItem) {
34        let mut lock = self.inner.lock().expect("mutex lock should succeed");
35        cfg_if::cfg_if! {
36            if #[cfg(feature = "data")] {
37                lock.insert(dest, (item.clone(), None));
38            }else {
39                let _ = item;
40                lock.insert(dest);
41            }
42        }
43    }
44
45    pub(crate) fn remove_item(&self, dest: &str) {
46        self.inner
47            .lock()
48            .expect("mutex lock should succeed")
49            .remove(dest);
50    }
51
52    pub(crate) fn clear_items(&self) -> Vec<String> {
53        let mut lock = self.inner.lock().expect("mutex lock should succeed");
54        cfg_if::cfg_if! {
55            if #[cfg(feature = "data")] {
56                lock.drain().map(|(k, _)| k).collect()
57            }else {
58                lock.drain().collect()
59            }
60        }
61    }
62
63    pub(crate) fn update_menu(&self, dest: &str, menu: &TrayMenu) {
64        cfg_if::cfg_if! {
65            if #[cfg(feature = "data")] {
66                if let Some((_, menu_cache)) = self.inner
67                        .lock()
68                        .expect("should get lock")
69                        .get_mut(dest) {
70                    menu_cache.replace(menu.clone());
71                } else {
72                    tracing::error!("could not find item in state");
73                }
74            }else {
75                let _ = menu;
76                let _ = dest;
77            }
78        }
79    }
80
81    #[cfg(feature = "data")]
82    pub(crate) fn apply_update_event(&self, dest: &str, event: &UpdateEvent) {
83        if let Some((item, menu)) = self
84            .inner
85            .lock()
86            .expect("mutex lock should succeed")
87            .get_mut(dest)
88        {
89            match event {
90                UpdateEvent::AttentionIcon(icon_name) => {
91                    item.attention_icon_name.clone_from(icon_name);
92                }
93                UpdateEvent::Icon {
94                    icon_name,
95                    icon_pixmap,
96                } => {
97                    item.icon_name.clone_from(icon_name);
98                    item.icon_pixmap.clone_from(icon_pixmap);
99                }
100                UpdateEvent::OverlayIcon(icon_name) => item.overlay_icon_name.clone_from(icon_name),
101                UpdateEvent::Status(status) => item.status = *status,
102                UpdateEvent::Title(title) => item.title.clone_from(title),
103                UpdateEvent::Tooltip(tooltip) => item.tool_tip.clone_from(tooltip),
104                UpdateEvent::Menu(tray_menu) => *menu = Some(tray_menu.clone()),
105                UpdateEvent::MenuConnect(menu) => item.menu = Some(menu.clone()),
106                UpdateEvent::MenuDiff(menu_diffs) => {
107                    if let Some(menu) = menu {
108                        apply_menu_diffs(menu, menu_diffs);
109                    }
110                }
111            }
112        } else {
113            error!("could not find item in state");
114        }
115    }
116}
117
118pub fn apply_menu_diffs(tray_menu: &mut TrayMenu, diffs: &[MenuDiff]) {
119    let mut diff_iter = diffs.iter().peekable();
120    tray_menu.submenus.iter_mut().for_each(|item| {
121        if let Some(diff) = diff_iter.next_if(|d| d.id == item.id) {
122            apply_menu_item_diff(item, &diff.update);
123        }
124    });
125}
126
127fn apply_menu_item_diff(menu_item: &mut MenuItem, update: &MenuItemUpdate) {
128    if let Some(label) = &update.label {
129        menu_item.label.clone_from(label);
130    }
131    if let Some(enabled) = update.enabled {
132        menu_item.enabled = enabled;
133    }
134    if let Some(visible) = update.visible {
135        menu_item.visible = visible;
136    }
137    if let Some(icon_name) = &update.icon_name {
138        menu_item.icon_name.clone_from(icon_name);
139    }
140    if let Some(icon_data) = &update.icon_data {
141        menu_item.icon_data.clone_from(icon_data);
142    }
143    if let Some(toggle_state) = update.toggle_state {
144        menu_item.toggle_state = toggle_state;
145    }
146    if let Some(disposition) = update.disposition {
147        menu_item.disposition = disposition;
148    }
149}