use std::{cell::RefCell, mem, rc::Rc, sync::Arc};
use crate::{
accelerator::{Accelerator, KeyAccelerator, MenuAccelerator},
platform_impl::PlatformMenuItem,
util, IsMenuItem, MenuId, MenuItemAction, MenuItemBuilder, MenuItemKind, StateCell, TextStyle,
};
#[derive(Clone)]
pub struct MenuItem {
pub(crate) id: Arc<MenuId>,
pub(crate) state: StateCell<MenuItemState>,
pub(crate) platform: Rc<RefCell<PlatformMenuItem>>,
}
#[derive(Debug, Clone)]
pub(crate) struct MenuItemState {
pub text: String,
pub enabled: bool,
pub accelerator: Option<MenuAccelerator>,
pub styled_text: Option<Vec<(String, TextStyle)>>,
}
impl crate::sealed::Sealed for MenuItem {}
impl IsMenuItem for MenuItem {
fn kind(&self) -> MenuItemKind {
MenuItemKind::MenuItem(self.clone())
}
fn id(&self) -> &MenuId {
self.id()
}
fn into_id(self) -> MenuId {
self.into_id()
}
}
impl MenuItem {
pub fn builder() -> MenuItemBuilder {
MenuItemBuilder::new()
}
pub fn new<S: AsRef<str>>(text: S, enabled: bool, accelerator: Option<Accelerator>) -> Self {
Self::new_inner(
None,
text.as_ref(),
enabled,
accelerator.map(MenuAccelerator::Physical),
)
}
pub fn with_id<I: Into<MenuId>, S: AsRef<str>>(
id: I,
text: S,
enabled: bool,
accelerator: Option<Accelerator>,
) -> Self {
Self::new_inner(
Some(id.into()),
text.as_ref(),
enabled,
accelerator.map(MenuAccelerator::Physical),
)
}
fn new_inner(
id: Option<MenuId>,
text: &str,
enabled: bool,
accelerator: Option<MenuAccelerator>,
) -> Self {
let id = util::next_id(id);
let state = MenuItemState {
text: text.to_string(),
enabled,
accelerator,
styled_text: None,
};
let click = MenuItemAction::Emit(id.clone());
let platform = PlatformMenuItem::new(click);
Self {
id: Arc::new(id),
state: StateCell::new(state),
platform: Rc::new(RefCell::new(platform)),
}
}
pub fn id(&self) -> &MenuId {
&self.id
}
pub fn text(&self) -> String {
let text = self.platform.borrow().text();
text.unwrap_or_else(|| self.state.borrow().text.clone())
}
pub fn set_text<S: AsRef<str>>(&self, text: S) {
let accelerator = {
let mut state = self.state.borrow_mut();
state.text = text.as_ref().to_string();
state.styled_text = None;
state.accelerator.clone()
};
self.platform
.borrow_mut()
.set_text(text.as_ref(), accelerator.as_ref())
}
pub fn set_styled_text<S: AsRef<str>>(&self, parts: impl IntoIterator<Item = (S, TextStyle)>) {
let parts = parts
.into_iter()
.map(|(text, style)| (text.as_ref().to_string(), style))
.collect::<Vec<_>>();
let (text, accelerator) = {
let mut state = self.state.borrow_mut();
state.text = parts.iter().map(|(text, _)| text.as_str()).collect();
state.styled_text = Some(parts.clone());
(state.text.clone(), state.accelerator.clone())
};
self.platform
.borrow_mut()
.set_styled_text(&text, &parts, accelerator.as_ref())
}
pub fn is_enabled(&self) -> bool {
let enabled = self.platform.borrow().is_enabled();
enabled.unwrap_or_else(|| self.state.borrow().enabled)
}
pub fn set_enabled(&self, enabled: bool) {
self.state.borrow_mut().enabled = enabled;
self.platform.borrow_mut().set_enabled(enabled)
}
pub fn set_accelerator(&self, accelerator: Option<Accelerator>) -> crate::Result<()> {
self.set_accelerator_inner(accelerator.map(MenuAccelerator::Physical))
}
pub fn set_key_accelerator(&self, accelerator: Option<KeyAccelerator>) -> crate::Result<()> {
self.set_accelerator_inner(accelerator.map(MenuAccelerator::Logical))
}
fn set_accelerator_inner(&self, accelerator: Option<MenuAccelerator>) -> crate::Result<()> {
let text = {
let mut state = self.state.borrow_mut();
state.accelerator = accelerator.clone();
state.text.clone()
};
self.platform
.borrow_mut()
.set_accelerator(&text, accelerator.as_ref())
}
pub fn into_id(mut self) -> MenuId {
if let Some(id) = Arc::get_mut(&mut self.id) {
mem::take(id)
} else {
self.id().clone()
}
}
}