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}