use std::{cell::RefCell, mem, rc::Rc};
use crate::{
accelerator::{Accelerator, CMD_OR_CTRL},
sealed::IsMenuItemBase,
AboutMetadata, IsMenuItem, MenuId, MenuItemKind,
};
use keyboard_types::{Code, Modifiers};
#[derive(Clone)]
pub struct PredefinedMenuItem {
pub(crate) id: Rc<MenuId>,
pub(crate) inner: Rc<RefCell<crate::platform_impl::MenuChild>>,
}
impl IsMenuItemBase for PredefinedMenuItem {}
impl IsMenuItem for PredefinedMenuItem {
fn kind(&self) -> MenuItemKind {
MenuItemKind::Predefined(self.clone())
}
fn id(&self) -> &MenuId {
self.id()
}
fn into_id(self) -> MenuId {
self.into_id()
}
}
impl PredefinedMenuItem {
pub fn separator() -> PredefinedMenuItem {
PredefinedMenuItem::new::<&str>(PredefinedMenuItemType::Separator, None)
}
pub fn copy(text: Option<&str>) -> PredefinedMenuItem {
PredefinedMenuItem::new(PredefinedMenuItemType::Copy, text)
}
pub fn cut(text: Option<&str>) -> PredefinedMenuItem {
PredefinedMenuItem::new(PredefinedMenuItemType::Cut, text)
}
pub fn paste(text: Option<&str>) -> PredefinedMenuItem {
PredefinedMenuItem::new(PredefinedMenuItemType::Paste, text)
}
pub fn select_all(text: Option<&str>) -> PredefinedMenuItem {
PredefinedMenuItem::new(PredefinedMenuItemType::SelectAll, text)
}
pub fn undo(text: Option<&str>) -> PredefinedMenuItem {
PredefinedMenuItem::new(PredefinedMenuItemType::Undo, text)
}
pub fn redo(text: Option<&str>) -> PredefinedMenuItem {
PredefinedMenuItem::new(PredefinedMenuItemType::Redo, text)
}
pub fn minimize(text: Option<&str>) -> PredefinedMenuItem {
PredefinedMenuItem::new(PredefinedMenuItemType::Minimize, text)
}
pub fn maximize(text: Option<&str>) -> PredefinedMenuItem {
PredefinedMenuItem::new(PredefinedMenuItemType::Maximize, text)
}
pub fn fullscreen(text: Option<&str>) -> PredefinedMenuItem {
PredefinedMenuItem::new(PredefinedMenuItemType::Fullscreen, text)
}
pub fn hide(text: Option<&str>) -> PredefinedMenuItem {
PredefinedMenuItem::new(PredefinedMenuItemType::Hide, text)
}
pub fn hide_others(text: Option<&str>) -> PredefinedMenuItem {
PredefinedMenuItem::new(PredefinedMenuItemType::HideOthers, text)
}
pub fn show_all(text: Option<&str>) -> PredefinedMenuItem {
PredefinedMenuItem::new(PredefinedMenuItemType::ShowAll, text)
}
pub fn close_window(text: Option<&str>) -> PredefinedMenuItem {
PredefinedMenuItem::new(PredefinedMenuItemType::CloseWindow, text)
}
pub fn quit(text: Option<&str>) -> PredefinedMenuItem {
PredefinedMenuItem::new(PredefinedMenuItemType::Quit, text)
}
pub fn about(text: Option<&str>, metadata: Option<AboutMetadata>) -> PredefinedMenuItem {
PredefinedMenuItem::new(PredefinedMenuItemType::About(metadata), text)
}
pub fn services(text: Option<&str>) -> PredefinedMenuItem {
PredefinedMenuItem::new(PredefinedMenuItemType::Services, text)
}
pub fn bring_all_to_front(text: Option<&str>) -> PredefinedMenuItem {
PredefinedMenuItem::new(PredefinedMenuItemType::BringAllToFront, text)
}
fn new<S: AsRef<str>>(item: PredefinedMenuItemType, text: Option<S>) -> Self {
let item = crate::platform_impl::MenuChild::new_predefined(
item,
text.map(|t| t.as_ref().to_string()),
);
Self {
id: Rc::new(item.id().clone()),
inner: Rc::new(RefCell::new(item)),
}
}
pub fn id(&self) -> &MenuId {
&self.id
}
pub fn text(&self) -> String {
self.inner.borrow().text()
}
pub fn set_text<S: AsRef<str>>(&self, text: S) {
self.inner.borrow_mut().set_text(text.as_ref())
}
pub fn into_id(mut self) -> MenuId {
if let Some(id) = Rc::get_mut(&mut self.id) {
mem::take(id)
} else {
self.id().clone()
}
}
}
#[test]
fn test_about_metadata() {
assert_eq!(
AboutMetadata {
..Default::default()
}
.full_version(),
None
);
assert_eq!(
AboutMetadata {
version: Some("Version: 1.inner".into()),
..Default::default()
}
.full_version(),
Some("Version: 1.inner".into())
);
assert_eq!(
AboutMetadata {
version: Some("Version: 1.inner".into()),
short_version: Some("Universal".into()),
..Default::default()
}
.full_version(),
Some("Version: 1.inner (Universal)".into())
);
}
#[derive(Debug, Clone)]
#[non_exhaustive]
#[allow(clippy::large_enum_variant)]
#[derive(Default)]
pub(crate) enum PredefinedMenuItemType {
Separator,
Copy,
Cut,
Paste,
SelectAll,
Undo,
Redo,
Minimize,
Maximize,
Fullscreen,
Hide,
HideOthers,
ShowAll,
CloseWindow,
Quit,
About(Option<AboutMetadata>),
Services,
BringAllToFront,
#[default]
None,
}
impl PredefinedMenuItemType {
pub(crate) fn text(&self) -> &str {
match self {
PredefinedMenuItemType::Separator => "",
PredefinedMenuItemType::Copy => "&Copy",
PredefinedMenuItemType::Cut => "Cu&t",
PredefinedMenuItemType::Paste => "&Paste",
PredefinedMenuItemType::SelectAll => "Select &All",
PredefinedMenuItemType::Undo => "Undo",
PredefinedMenuItemType::Redo => "Redo",
PredefinedMenuItemType::Minimize => "&Minimize",
#[cfg(target_os = "macos")]
PredefinedMenuItemType::Maximize => "Zoom",
#[cfg(not(target_os = "macos"))]
PredefinedMenuItemType::Maximize => "Ma&ximize",
PredefinedMenuItemType::Fullscreen => "Toggle Full Screen",
PredefinedMenuItemType::Hide => "&Hide",
PredefinedMenuItemType::HideOthers => "Hide Others",
PredefinedMenuItemType::ShowAll => "Show All",
#[cfg(windows)]
PredefinedMenuItemType::CloseWindow => "Close",
#[cfg(not(windows))]
PredefinedMenuItemType::CloseWindow => "C&lose Window",
#[cfg(windows)]
PredefinedMenuItemType::Quit => "&Exit",
#[cfg(not(windows))]
PredefinedMenuItemType::Quit => "&Quit",
PredefinedMenuItemType::About(_) => "&About",
PredefinedMenuItemType::Services => "Services",
PredefinedMenuItemType::BringAllToFront => "Bring All to Front",
PredefinedMenuItemType::None => "",
}
}
pub(crate) fn accelerator(&self) -> Option<Accelerator> {
match self {
PredefinedMenuItemType::Copy => Some(Accelerator::new(Some(CMD_OR_CTRL), Code::KeyC)),
PredefinedMenuItemType::Cut => Some(Accelerator::new(Some(CMD_OR_CTRL), Code::KeyX)),
PredefinedMenuItemType::Paste => Some(Accelerator::new(Some(CMD_OR_CTRL), Code::KeyV)),
PredefinedMenuItemType::Undo => Some(Accelerator::new(Some(CMD_OR_CTRL), Code::KeyZ)),
#[cfg(target_os = "macos")]
PredefinedMenuItemType::Redo => Some(Accelerator::new(
Some(CMD_OR_CTRL | Modifiers::SHIFT),
Code::KeyZ,
)),
#[cfg(not(target_os = "macos"))]
PredefinedMenuItemType::Redo => Some(Accelerator::new(Some(CMD_OR_CTRL), Code::KeyY)),
PredefinedMenuItemType::SelectAll => {
Some(Accelerator::new(Some(CMD_OR_CTRL), Code::KeyA))
}
PredefinedMenuItemType::Minimize => {
Some(Accelerator::new(Some(CMD_OR_CTRL), Code::KeyM))
}
#[cfg(target_os = "macos")]
PredefinedMenuItemType::Fullscreen => Some(Accelerator::new(
Some(Modifiers::META | Modifiers::CONTROL),
Code::KeyF,
)),
PredefinedMenuItemType::Hide => Some(Accelerator::new(Some(CMD_OR_CTRL), Code::KeyH)),
PredefinedMenuItemType::HideOthers => Some(Accelerator::new(
Some(CMD_OR_CTRL | Modifiers::ALT),
Code::KeyH,
)),
#[cfg(target_os = "macos")]
PredefinedMenuItemType::CloseWindow => {
Some(Accelerator::new(Some(CMD_OR_CTRL), Code::KeyW))
}
#[cfg(not(target_os = "macos"))]
PredefinedMenuItemType::CloseWindow => {
Some(Accelerator::new(Some(Modifiers::ALT), Code::F4))
}
#[cfg(target_os = "macos")]
PredefinedMenuItemType::Quit => Some(Accelerator::new(Some(CMD_OR_CTRL), Code::KeyQ)),
_ => None,
}
}
}