use crate::{
item::StatusNotifierItem,
menu::{MenuDiff, MenuItem, MenuItemUpdate, TrayMenu},
};
use std::sync::{Arc, Mutex};
#[cfg(feature = "data")]
use {crate::client::UpdateEvent, tracing::error};
#[cfg(feature = "data")]
pub type BaseMap = std::collections::HashMap<String, (StatusNotifierItem, Option<TrayMenu>)>;
#[cfg(not(feature = "data"))]
type BaseMap = std::collections::HashSet<String>;
#[derive(Debug, Clone)]
pub(crate) struct TrayItemMap {
inner: Arc<Mutex<BaseMap>>,
}
impl TrayItemMap {
pub(crate) fn new() -> Self {
Self {
inner: Arc::new(Mutex::new(BaseMap::default())),
}
}
#[cfg(feature = "data")]
pub(crate) fn get_map(&self) -> Arc<Mutex<BaseMap>> {
self.inner.clone()
}
pub(crate) fn new_item(&self, dest: String, item: &StatusNotifierItem) {
let mut lock = self.inner.lock().expect("mutex lock should succeed");
cfg_if::cfg_if! {
if #[cfg(feature = "data")] {
lock.insert(dest, (item.clone(), None));
}else {
let _ = item;
lock.insert(dest);
}
}
}
pub(crate) fn remove_item(&self, dest: &str) {
self.inner
.lock()
.expect("mutex lock should succeed")
.remove(dest);
}
pub(crate) fn clear_items(&self) -> Vec<String> {
let mut lock = self.inner.lock().expect("mutex lock should succeed");
cfg_if::cfg_if! {
if #[cfg(feature = "data")] {
lock.drain().map(|(k, _)| k).collect()
}else {
lock.drain().collect()
}
}
}
pub(crate) fn update_menu(&self, dest: &str, menu: &TrayMenu) {
cfg_if::cfg_if! {
if #[cfg(feature = "data")] {
if let Some((_, menu_cache)) = self.inner
.lock()
.expect("should get lock")
.get_mut(dest) {
menu_cache.replace(menu.clone());
} else {
tracing::error!("could not find item in state");
}
}else {
let _ = menu;
let _ = dest;
}
}
}
#[cfg(feature = "data")]
pub(crate) fn apply_update_event(&self, dest: &str, event: &UpdateEvent) {
if let Some((item, menu)) = self
.inner
.lock()
.expect("mutex lock should succeed")
.get_mut(dest)
{
match event {
UpdateEvent::AttentionIcon(icon_name) => {
item.attention_icon_name.clone_from(icon_name);
}
UpdateEvent::Icon {
icon_name,
icon_pixmap,
} => {
item.icon_name.clone_from(icon_name);
item.icon_pixmap.clone_from(icon_pixmap);
}
UpdateEvent::OverlayIcon(icon_name) => item.overlay_icon_name.clone_from(icon_name),
UpdateEvent::Status(status) => item.status = *status,
UpdateEvent::Title(title) => item.title.clone_from(title),
UpdateEvent::Tooltip(tooltip) => item.tool_tip.clone_from(tooltip),
UpdateEvent::Menu(tray_menu) => *menu = Some(tray_menu.clone()),
UpdateEvent::MenuConnect(menu) => item.menu = Some(menu.clone()),
UpdateEvent::MenuDiff(menu_diffs) => {
if let Some(menu) = menu {
apply_menu_diffs(menu, menu_diffs);
}
}
}
} else {
error!("could not find item in state");
}
}
}
pub fn apply_menu_diffs(tray_menu: &mut TrayMenu, diffs: &[MenuDiff]) {
let mut diff_iter = diffs.iter().peekable();
tray_menu.submenus.iter_mut().for_each(|item| {
if let Some(diff) = diff_iter.next_if(|d| d.id == item.id) {
apply_menu_item_diff(item, &diff.update);
}
});
}
fn apply_menu_item_diff(menu_item: &mut MenuItem, update: &MenuItemUpdate) {
if let Some(label) = &update.label {
menu_item.label.clone_from(label);
}
if let Some(enabled) = update.enabled {
menu_item.enabled = enabled;
}
if let Some(visible) = update.visible {
menu_item.visible = visible;
}
if let Some(icon_name) = &update.icon_name {
menu_item.icon_name.clone_from(icon_name);
}
if let Some(icon_data) = &update.icon_data {
menu_item.icon_data.clone_from(icon_data);
}
if let Some(toggle_state) = update.toggle_state {
menu_item.toggle_state = toggle_state;
}
if let Some(disposition) = update.disposition {
menu_item.disposition = disposition;
}
}