pub mod config;
mod platform;
use std::{
path::{Path, PathBuf},
sync::{LazyLock, OnceLock},
};
#[cfg(target_os = "linux")]
use config::Corner;
pub use platform::platform_impl::{Menu, MenuBuilder, MenuItem};
use serde::{Deserialize, Serialize};
use smol::channel::{unbounded, Receiver, Sender};
#[derive(Debug, PartialEq, Eq)]
enum ThemeChangeFactor {
SystemSetting,
User,
App,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum MenuType {
Main,
Submenu,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)]
pub enum MenuItemType {
Text,
Checkbox,
Radio,
Submenu,
Separator,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub(crate) struct PathIcon {
path: PathBuf,
width: u32,
height: u32,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub(crate) struct DataIcon {
data: Vec<u8>,
width: u32,
height: u32,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct SvgIcon {
data: String,
width: u32,
height: u32,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) enum MenuIconKind {
Path(PathIcon),
Data(DataIcon),
Svg(SvgIcon),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct MenuIcon {
pub(crate) icon: MenuIconKind,
}
impl MenuIcon {
pub fn new<P: AsRef<Path>>(path: P, width: u32, height: u32) -> Self {
Self {
icon: MenuIconKind::Path(PathIcon {
path: path.as_ref().to_path_buf(),
width,
height,
}),
}
}
pub fn from_data(data: Vec<u8>, width: u32, height: u32) -> Self {
Self {
icon: MenuIconKind::Data(DataIcon {
data,
width,
height,
}),
}
}
pub fn from_svg(svg: String, width: u32, height: u32) -> Self {
Self {
icon: MenuIconKind::Svg(SvgIcon {
data: svg,
width,
height,
}),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MenuEvent {
pub item: MenuItem,
}
pub(crate) struct InnerMenuEvent {
pub(crate) item: Option<MenuItem>,
}
pub type MenuEventReceiver = Receiver<MenuEvent>;
type MenuEventHandler = std::boxed::Box<dyn Fn(MenuEvent) + Send + Sync + 'static>;
type InnerMenuEventReceiver = Receiver<InnerMenuEvent>;
static MENU_CHANNEL: LazyLock<(Sender<MenuEvent>, MenuEventReceiver)> = LazyLock::new(unbounded);
static INNER_MENU_CHANNEL: LazyLock<(Sender<InnerMenuEvent>, InnerMenuEventReceiver)> = LazyLock::new(unbounded);
static MENU_EVENT_HANDLER: OnceLock<Option<MenuEventHandler>> = OnceLock::new();
impl MenuEvent {
pub fn item(&self) -> &MenuItem {
&self.item
}
pub fn receiver<'a>() -> &'a MenuEventReceiver {
&MENU_CHANNEL.1
}
fn innner_receiver<'a>() -> &'a InnerMenuEventReceiver {
&INNER_MENU_CHANNEL.1
}
pub fn set_event_handler<F: Fn(MenuEvent) + Send + Sync + 'static>(f: Option<F>) {
if let Some(f) = f {
let _ = MENU_EVENT_HANDLER.set(Some(std::boxed::Box::new(f)));
} else {
let _ = MENU_EVENT_HANDLER.set(None);
}
}
fn send(event: MenuEvent) {
if let Some(handler) = MENU_EVENT_HANDLER.get_or_init(|| None) {
handler(event);
} else {
let _ = MENU_CHANNEL.0.send_blocking(event);
}
}
fn send_inner(event: InnerMenuEvent) {
let _ = INNER_MENU_CHANNEL.0.send_blocking(event);
}
}