use crate::menu::{Icon, Item, Menu, MenuId};
use crate::Tray;
pub(crate) struct MuriSni {
pub(crate) tray: Tray,
pub(crate) visible: bool,
}
impl MuriSni {
fn dispatch(&self, id: &MenuId) {
if !id.is_none() {
self.tray.dispatch(id);
}
}
}
fn png_to_argb32(bytes: &[u8]) -> Option<ksni::Icon> {
let (rgba, width, height) = crate::render::decode_png(bytes)?;
let mut data = Vec::with_capacity((width as usize) * (height as usize) * 4);
for px in rgba.chunks_exact(4) {
data.push(px[3]); data.push(px[0]); data.push(px[1]); data.push(px[2]); }
Some(ksni::Icon {
width: width as i32,
height: height as i32,
data,
})
}
fn leading_png(icon: &Option<Icon>) -> Vec<u8> {
match icon {
Some(Icon::Png(bytes)) => bytes.to_vec(),
_ => Vec::new(),
}
}
fn build_items(menu: &Menu) -> Vec<ksni::menu::MenuItem<MuriSni>> {
use ksni::menu::{CheckmarkItem, MenuItem, StandardItem, SubMenu};
menu.items
.iter()
.map(|item| match item {
Item::Separator => MenuItem::Separator,
Item::SectionHeader(row) => {
StandardItem {
label: row.accessible_name(),
enabled: false,
icon_data: leading_png(&row.leading),
..Default::default()
}
.into()
}
Item::Row(row) => {
let id = row.id.clone();
let label = row.accessible_name();
let icon_data = leading_png(&row.leading);
let enabled = row.enabled && !row.id.is_none();
if let Some(checked) = row.checked {
CheckmarkItem {
label,
enabled,
checked,
icon_data,
activate: Box::new(move |s: &mut MuriSni| s.dispatch(&id)),
..Default::default()
}
.into()
} else {
StandardItem {
label,
enabled,
icon_data,
activate: Box::new(move |s: &mut MuriSni| s.dispatch(&id)),
..Default::default()
}
.into()
}
}
Item::Submenu { label, menu } => SubMenu {
label: label.accessible_name(),
enabled: label.enabled,
icon_data: leading_png(&label.leading),
submenu: build_items(menu),
..Default::default()
}
.into(),
})
.collect()
}
impl ksni::Tray for MuriSni {
const MENU_ON_ACTIVATE: bool = true;
fn id(&self) -> String {
"muri".to_owned()
}
fn title(&self) -> String {
self.tray
.tooltip
.clone()
.unwrap_or_else(|| "muri".to_owned())
}
fn icon_name(&self) -> String {
String::new()
}
fn icon_pixmap(&self) -> Vec<ksni::Icon> {
match &self.tray.icon {
Icon::Png(bytes) | Icon::Svg(bytes) => png_to_argb32(bytes).into_iter().collect(),
Icon::Checkmark | Icon::Symbol(_) => Vec::new(),
}
}
fn tool_tip(&self) -> ksni::ToolTip {
ksni::ToolTip {
title: self.tray.tooltip.clone().unwrap_or_default(),
..Default::default()
}
}
fn status(&self) -> ksni::Status {
if self.visible {
ksni::Status::Active
} else {
ksni::Status::Passive
}
}
fn menu(&self) -> Vec<ksni::menu::MenuItem<Self>> {
build_items(&self.tray.menu)
}
}