use std::{cell::RefCell, mem, rc::Rc, sync::Arc};
use crate::{
accelerator::{Accelerator, KeyAccelerator, MenuAccelerator},
platform_impl::PlatformMenuItem,
util, CheckMenuItemBuilder, IsMenuItem, MenuId, MenuItemAction, MenuItemKind, StateCell,
TextStyle,
};
#[derive(Clone)]
pub struct CheckMenuItem {
pub(crate) id: Arc<MenuId>,
pub(crate) state: StateCell<CheckMenuItemState>,
pub(crate) platform: Rc<RefCell<PlatformMenuItem>>,
}
#[derive(Debug, Clone)]
pub(crate) struct CheckMenuItemState {
pub text: String,
pub enabled: bool,
pub checked: bool,
pub accelerator: Option<MenuAccelerator>,
pub styled_text: Option<Vec<(String, TextStyle)>>,
}
impl crate::sealed::Sealed for CheckMenuItem {}
impl IsMenuItem for CheckMenuItem {
fn kind(&self) -> MenuItemKind {
MenuItemKind::Check(self.clone())
}
fn id(&self) -> &MenuId {
self.id()
}
fn into_id(self) -> MenuId {
self.into_id()
}
}
impl CheckMenuItem {
pub fn builder() -> CheckMenuItemBuilder {
CheckMenuItemBuilder::new()
}
pub fn new<S: AsRef<str>>(
text: S,
enabled: bool,
checked: bool,
accelerator: Option<Accelerator>,
) -> Self {
Self::new_inner(
None,
text.as_ref(),
enabled,
checked,
accelerator.map(MenuAccelerator::Physical),
)
}
pub fn with_id<I: Into<MenuId>, S: AsRef<str>>(
id: I,
text: S,
enabled: bool,
checked: bool,
accelerator: Option<Accelerator>,
) -> Self {
Self::new_inner(
Some(id.into()),
text.as_ref(),
enabled,
checked,
accelerator.map(MenuAccelerator::Physical),
)
}
fn new_inner(
id: Option<MenuId>,
text: &str,
enabled: bool,
checked: bool,
accelerator: Option<MenuAccelerator>,
) -> Self {
let id = util::next_id(id);
let state = StateCell::new(CheckMenuItemState {
text: text.to_string(),
enabled,
checked,
accelerator,
styled_text: None,
});
let click = MenuItemAction::Toggle(id.clone(), state.downgrade());
let platform = PlatformMenuItem::new(click);
Self {
id: Arc::new(id),
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 is_checked(&self) -> bool {
let checked = self.platform.borrow().is_checked();
checked.unwrap_or_else(|| self.state.borrow().checked)
}
pub fn set_checked(&self, checked: bool) {
self.state.borrow_mut().checked = checked;
self.platform.borrow_mut().set_checked(checked)
}
pub fn into_id(mut self) -> MenuId {
if let Some(id) = Arc::get_mut(&mut self.id) {
mem::take(id)
} else {
self.id().clone()
}
}
}