use bevy::asset::{Assets, Handle};
use bevy::prelude::{Deref, DerefMut, Image, Resource};
use tray_icon::{menu, Icon};
pub use menu_item::MenuItem;
mod menu_item;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Resource)]
pub struct TrayIcon {
pub icon: Option<Handle<Image>>,
pub tooltip: Option<String>,
pub menu: Menu,
pub show_menu_on_left_click: bool,
}
impl TrayIcon {
pub(crate) fn as_icon(&self, images: &Assets<Image>) -> Option<Icon> {
let id = self.icon.as_ref().map(|handle| handle.id())?;
let image = images.get(id)?;
Icon::from_rgba(image.data.clone()?, image.width(), image.height()).ok()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Deref, DerefMut)]
pub struct Menu(Vec<MenuItem>);
impl Menu {
#[inline]
pub const fn new(items: Vec<MenuItem>) -> Self {
Self(items)
}
pub fn set_enable(&mut self, menu_id: &str, enable: bool) -> crate::error::Result {
for menu in self.0.iter_mut() {
match menu {
MenuItem::Common { id, enabled, .. } => {
if id.0 == menu_id {
*enabled = enable;
return Ok(());
}
}
MenuItem::SubMenu {
id, enabled, menu, ..
} => {
if id.0 == menu_id {
*enabled = enable;
return Ok(());
} else if menu.set_enable(menu_id, enable).is_ok() {
return Ok(());
}
}
_ => {}
}
}
Err(crate::error::Error::NotFoundMenu(menu_id.to_string()))
}
pub(crate) fn as_context(&self) -> Option<Box<dyn menu::ContextMenu>> {
if self.0.is_empty() {
None
} else {
let menu = menu::Menu::new();
for item in self.0.iter().map(|item| item.as_dyn()) {
menu.append(&*item).unwrap();
}
Some(Box::new(menu))
}
}
}
#[repr(transparent)]
#[derive(Deref)]
pub(crate) struct RealTrayIcon(pub(crate) tray_icon::TrayIcon);
#[cfg(test)]
mod tests {
use crate::resource::{Menu, MenuItem};
#[test]
fn enable_common() {
let mut menu = Menu::new(vec![MenuItem::common("test", "test", false, None)]);
menu.set_enable("test", true).unwrap();
assert!(check_enabled("test", &menu));
}
#[test]
fn enable_submenu() {
let mut menu = Menu::new(vec![
MenuItem::common("test", "test", false, None),
MenuItem::submenu("sub", "sub", false, Menu::default()),
]);
menu.set_enable("sub", true).unwrap();
assert!(check_enabled("sub", &menu));
}
#[test]
fn enable_item_in_submenu() {
let mut menu = Menu::new(vec![
MenuItem::common("test", "test", false, None),
MenuItem::submenu(
"sub",
"sub",
false,
Menu::new(vec![MenuItem::common("sub_item", "sub_item", false, None)]),
),
]);
menu.set_enable("sub_item", true).unwrap();
assert!(check_enabled("sub_item", &menu));
}
fn check_enabled(target_id: &str, menu: &Menu) -> bool {
for item in menu.0.iter() {
match item {
MenuItem::SubMenu {
id, menu, enabled, ..
} => {
if id == target_id {
return *enabled;
} else if check_enabled(target_id, menu) {
return true;
}
}
MenuItem::Common { id, enabled, .. } => {
if id == target_id {
return *enabled;
}
}
_ => {}
}
}
false
}
}