use crate::Separator;
use crate::adapt::MapAny;
use kas::prelude::*;
use std::fmt::Debug;
mod menu_entry;
mod menubar;
mod submenu;
pub use menu_entry::{MenuEntry, MenuToggle};
pub use menubar::{MenuBar, MenuBuilder};
pub use submenu::SubMenu;
#[derive(Default)]
pub struct SubItems<'a> {
pub label: Option<&'a mut dyn Tile>,
pub label2: Option<&'a mut dyn Tile>,
pub submenu: Option<&'a mut dyn Tile>,
pub icon: Option<&'a mut dyn Tile>,
pub toggle: Option<&'a mut dyn Tile>,
}
#[autoimpl(for<T: trait + ?Sized> Box<T>)]
pub trait Menu: Widget {
fn sub_items(&mut self) -> Option<SubItems<'_>> {
None
}
fn menu_is_open(&self) -> bool {
false
}
fn set_menu_path(
&mut self,
cx: &mut EventCx,
data: &Self::Data,
target: Option<&Id>,
set_focus: bool,
) {
let _ = (cx, data, target, set_focus);
}
}
impl<A, W: Menu<Data = ()>> Menu for MapAny<A, W> {
fn sub_items(&mut self) -> Option<SubItems<'_>> {
self.inner.sub_items()
}
fn menu_is_open(&self) -> bool {
self.inner.menu_is_open()
}
fn set_menu_path(&mut self, cx: &mut EventCx, _: &A, target: Option<&Id>, set_focus: bool) {
self.inner.set_menu_path(cx, &(), target, set_focus);
}
}
pub type BoxedMenu<Data> = Box<dyn Menu<Data = Data>>;
pub struct SubMenuBuilder<'a, Data> {
menu: &'a mut Vec<BoxedMenu<Data>>,
}
impl<'a, Data> SubMenuBuilder<'a, Data> {
#[inline]
pub fn push_item(&mut self, item: BoxedMenu<Data>) {
self.menu.push(item);
}
#[inline]
pub fn item(mut self, item: BoxedMenu<Data>) -> Self {
self.push_item(item);
self
}
}
impl<'a, Data: 'static> SubMenuBuilder<'a, Data> {
pub fn push_entry<S: Into<AccessString>, M>(&mut self, label: S, msg: M)
where
M: Clone + Debug + 'static,
{
self.menu
.push(Box::new(MapAny::new(MenuEntry::new_msg(label, msg))));
}
#[inline]
pub fn entry<S: Into<AccessString>, M>(mut self, label: S, msg: M) -> Self
where
M: Clone + Debug + 'static,
{
self.push_entry(label, msg);
self
}
pub fn push_toggle<M: Debug + 'static>(
&mut self,
label: impl Into<AccessString>,
state_fn: impl Fn(&ConfigCx, &Data) -> bool + 'static,
msg_fn: impl Fn(bool) -> M + 'static,
) {
self.menu
.push(Box::new(MenuToggle::new_msg(label, state_fn, msg_fn)));
}
pub fn toggle<M: Debug + 'static>(
mut self,
label: impl Into<AccessString>,
state_fn: impl Fn(&ConfigCx, &Data) -> bool + 'static,
msg_fn: impl Fn(bool) -> M + 'static,
) -> Self {
self.push_toggle(label, state_fn, msg_fn);
self
}
pub fn push_separator(&mut self) {
self.menu.push(Box::new(MapAny::new(Separator::new())));
}
#[inline]
pub fn separator(mut self) -> Self {
self.push_separator();
self
}
pub fn push_submenu<F>(&mut self, label: impl Into<AccessString>, f: F)
where
F: FnOnce(SubMenuBuilder<Data>),
{
self.push_submenu_dir(label, f, Direction::Right);
}
pub fn submenu<F>(mut self, label: impl Into<AccessString>, f: F) -> Self
where
F: FnOnce(SubMenuBuilder<Data>),
{
self.push_submenu(label, f);
self
}
pub fn push_submenu_dir<F>(&mut self, label: impl Into<AccessString>, f: F, dir: Direction)
where
F: FnOnce(SubMenuBuilder<Data>),
{
let mut menu = Vec::new();
f(SubMenuBuilder { menu: &mut menu });
self.menu
.push(Box::new(SubMenu::<false, _>::new(label, menu, dir)));
}
#[inline]
pub fn submenu_dir<F>(mut self, label: impl Into<AccessString>, f: F, dir: Direction) -> Self
where
F: FnOnce(SubMenuBuilder<Data>),
{
self.push_submenu_dir(label, f, dir);
self
}
}