use crate::{sealed, Icon, MenuId, NativeIcon, WeakStateCell};
mod check;
mod icon;
mod menu;
mod normal;
mod predefined;
mod submenu;
pub use check::*;
pub use icon::*;
pub use menu::*;
pub use normal::*;
pub use predefined::*;
pub use submenu::*;
pub trait IsMenuItem: sealed::Sealed {
fn kind(&self) -> MenuItemKind;
fn id(&self) -> &MenuId;
fn into_id(self) -> MenuId;
}
#[derive(Clone)]
pub enum MenuItemKind {
MenuItem(MenuItem),
Submenu(Submenu),
Predefined(PredefinedMenuItem),
Check(CheckMenuItem),
Icon(IconMenuItem),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum TextStyle {
#[default]
Default,
Secondary,
}
pub(crate) struct UnsafeMenuItemKind(std::mem::ManuallyDrop<MenuItemKind>);
unsafe impl Send for UnsafeMenuItemKind {}
impl UnsafeMenuItemKind {
pub(crate) fn new(local: MenuItemKind) -> Self {
Self(std::mem::ManuallyDrop::new(local))
}
pub(crate) unsafe fn borrow(&self) -> &MenuItemKind {
&self.0
}
pub(crate) unsafe fn clone(&self) -> MenuItemKind {
unsafe { self.borrow() }.clone()
}
pub(crate) unsafe fn unwrap(mut self) -> MenuItemKind {
unsafe { std::mem::ManuallyDrop::take(&mut self.0) }
}
#[cfg(feature = "snapshot")]
pub(crate) fn snapshot(&self) -> crate::MenuItemKindSnapshot {
crate::MenuItemKindSnapshot::from(&*self.0)
}
}
impl MenuItemKind {
#[cfg(feature = "snapshot")]
pub fn snapshot(&self) -> crate::MenuItemKindSnapshot {
self.into()
}
pub fn id(&self) -> &MenuId {
match self {
MenuItemKind::MenuItem(i) => i.id(),
MenuItemKind::Submenu(i) => i.id(),
MenuItemKind::Predefined(i) => i.id(),
MenuItemKind::Check(i) => i.id(),
MenuItemKind::Icon(i) => i.id(),
}
}
pub fn as_menuitem(&self) -> Option<&MenuItem> {
match self {
MenuItemKind::MenuItem(i) => Some(i),
_ => None,
}
}
pub fn as_menuitem_unchecked(&self) -> &MenuItem {
match self {
MenuItemKind::MenuItem(i) => i,
_ => panic!("Not a MenuItem"),
}
}
pub fn as_submenu(&self) -> Option<&Submenu> {
match self {
MenuItemKind::Submenu(i) => Some(i),
_ => None,
}
}
pub fn as_submenu_unchecked(&self) -> &Submenu {
match self {
MenuItemKind::Submenu(i) => i,
_ => panic!("Not a Submenu"),
}
}
pub fn as_predefined_menuitem(&self) -> Option<&PredefinedMenuItem> {
match self {
MenuItemKind::Predefined(i) => Some(i),
_ => None,
}
}
pub fn as_predefined_menuitem_unchecked(&self) -> &PredefinedMenuItem {
match self {
MenuItemKind::Predefined(i) => i,
_ => panic!("Not a PredefinedMenuItem"),
}
}
pub fn as_check_menuitem(&self) -> Option<&CheckMenuItem> {
match self {
MenuItemKind::Check(i) => Some(i),
_ => None,
}
}
pub fn as_check_menuitem_unchecked(&self) -> &CheckMenuItem {
match self {
MenuItemKind::Check(i) => i,
_ => panic!("Not a CheckMenuItem"),
}
}
pub fn as_icon_menuitem(&self) -> Option<&IconMenuItem> {
match self {
MenuItemKind::Icon(i) => Some(i),
_ => None,
}
}
pub fn as_icon_menuitem_unchecked(&self) -> &IconMenuItem {
match self {
MenuItemKind::Icon(i) => i,
_ => panic!("Not an IconMenuItem"),
}
}
pub fn into_id(self) -> MenuId {
match self {
MenuItemKind::MenuItem(i) => i.into_id(),
MenuItemKind::Submenu(i) => i.into_id(),
MenuItemKind::Predefined(i) => i.into_id(),
MenuItemKind::Check(i) => i.into_id(),
MenuItemKind::Icon(i) => i.into_id(),
}
}
}
#[derive(Debug, Clone)]
pub(crate) enum IconType {
Custom(Icon),
Native(NativeIcon),
}
#[derive(Clone)]
pub(crate) enum MenuItemAction {
Emit(MenuId),
Toggle(MenuId, WeakStateCell<CheckMenuItemState>),
Predefined(WeakStateCell<PredefinedMenuItemState>),
}
#[cfg(test)]
mod tests {
use crate::{CheckMenuItem, IconMenuItem, MenuId, MenuItem, PredefinedMenuItem, Submenu};
#[test]
#[cfg_attr(
all(
miri,
not(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))
),
ignore
)]
fn it_returns_same_id() {
let id = MenuId::new("1");
assert_eq!(id, MenuItem::with_id(id.clone(), "", true, None).id());
assert_eq!(id, Submenu::with_id(id.clone(), "", true).id());
assert_eq!(
id,
CheckMenuItem::with_id(id.clone(), "", true, true, None).id()
);
assert_eq!(
id,
IconMenuItem::with_id(id.clone(), "", true, None, None).id()
);
}
#[test]
#[cfg_attr(
all(
miri,
not(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))
),
ignore
)]
fn test_convert_from_id_and_into_id() {
let id = "TEST ID";
let expected = MenuId(id.to_string());
let item = CheckMenuItem::with_id(id, "test", true, true, None);
assert_eq!(item.id(), &expected);
assert_eq!(item.into_id(), expected);
let item = IconMenuItem::with_id(id, "test", true, None, None);
assert_eq!(item.id(), &expected);
assert_eq!(item.into_id(), expected);
let item = MenuItem::with_id(id, "test", true, None);
assert_eq!(item.id(), &expected);
assert_eq!(item.into_id(), expected);
let item = Submenu::with_id(id, "test", true);
assert_eq!(item.id(), &expected);
assert_eq!(item.into_id(), expected);
let item = PredefinedMenuItem::separator();
assert_eq!(item.id().clone(), item.into_id());
}
}