use super::{Menu, SubItems};
use crate::{AccessLabel, CheckBox};
use kas::prelude::*;
use kas::theme::{FrameStyle, TextClass};
use std::fmt::Debug;
#[impl_self]
mod MenuEntry {
#[derive(Debug)]
#[widget]
#[layout(self.label)]
pub struct MenuEntry<M: Clone + Debug + 'static> {
core: widget_core!(),
#[widget]
label: AccessLabel,
msg: M,
}
impl Layout for Self {
fn draw(&self, mut draw: DrawCx) {
draw.frame(self.rect(), FrameStyle::MenuEntry, Default::default());
self.label.draw(draw.re());
}
}
impl Tile for Self {
fn navigable(&self) -> bool {
true
}
fn role(&self, cx: &mut dyn RoleCx) -> Role<'_> {
cx.set_label(self.label.id());
Role::Button
}
}
impl Self {
pub fn new_msg<S: Into<AccessString>>(label: S, msg: M) -> Self {
MenuEntry {
core: Default::default(),
label: AccessLabel::new(label).with_class(TextClass::Label),
msg,
}
}
pub fn set_msg(&mut self, msg: M) {
self.msg = msg;
}
pub fn as_str(&self) -> &str {
self.label.as_str()
}
}
impl Events for Self {
type Data = ();
fn probe(&self, _: Coord) -> Id {
self.id()
}
fn handle_event(&mut self, cx: &mut EventCx, _: &Self::Data, event: Event) -> IsUsed {
match event {
Event::Command(cmd, code) if cmd.is_activate() => {
cx.push(self.msg.clone());
cx.depress_with_key(&self, code);
Used
}
_ => Unused,
}
}
fn handle_messages(&mut self, cx: &mut EventCx, _: &Self::Data) {
if let Some(kas::messages::Activate(code)) = cx.try_pop() {
cx.push(self.msg.clone());
cx.depress_with_key(&self, code);
}
}
}
impl Menu for Self {
fn sub_items(&mut self) -> Option<SubItems<'_>> {
Some(SubItems {
label: Some(&mut self.label),
..Default::default()
})
}
}
impl PartialEq<M> for Self
where
M: PartialEq,
{
#[inline]
fn eq(&self, rhs: &M) -> bool {
self.msg == *rhs
}
}
}
#[impl_self]
mod MenuToggle {
#[widget]
#[layout(row! [self.checkbox, self.label])]
pub struct MenuToggle<A> {
core: widget_core!(),
#[widget]
checkbox: CheckBox<A>,
#[widget(&())]
label: AccessLabel,
}
impl Layout for Self {
fn draw(&self, mut draw: DrawCx) {
draw.set_id(self.checkbox.id());
draw.frame(self.rect(), FrameStyle::MenuEntry, Default::default());
kas::MacroDefinedLayout::draw(self, draw);
}
}
impl Tile for Self {
fn role_child_properties(&self, cx: &mut dyn RoleCx, index: usize) {
if index == widget_index!(self.checkbox) {
cx.set_label(self.label.id());
}
}
}
impl Events for Self {
type Data = A;
fn probe(&self, _: Coord) -> Id {
self.checkbox.id()
}
fn post_configure(&mut self, _: &mut ConfigCx) {
self.label.set_target(self.checkbox.id());
}
fn handle_messages(&mut self, cx: &mut EventCx, data: &Self::Data) {
if let Some(kas::messages::Activate(code)) = cx.try_pop() {
self.checkbox.toggle(cx, data);
cx.depress_with_key(&self, code);
}
}
}
impl Menu for Self {
fn sub_items(&mut self) -> Option<SubItems<'_>> {
Some(SubItems {
label: Some(&mut self.label),
toggle: Some(&mut self.checkbox),
..Default::default()
})
}
}
impl Self {
#[inline]
pub fn new(
label: impl Into<AccessString>,
state_fn: impl Fn(&ConfigCx, &A) -> bool + 'static,
) -> Self {
MenuToggle {
core: Default::default(),
checkbox: CheckBox::new(state_fn),
label: AccessLabel::new(label).with_class(TextClass::Label),
}
}
#[inline]
#[must_use]
pub fn with<F>(self, f: F) -> Self
where
F: Fn(&mut EventCx, &A, bool) + 'static,
{
MenuToggle {
core: self.core,
checkbox: self.checkbox.with(f),
label: self.label,
}
}
#[inline]
#[must_use]
pub fn with_msg<M>(self, f: impl Fn(bool) -> M + 'static) -> Self
where
M: std::fmt::Debug + 'static,
{
self.with(move |cx, _, state| cx.push(f(state)))
}
#[inline]
pub fn new_msg<M: Debug + 'static>(
label: impl Into<AccessString>,
state_fn: impl Fn(&ConfigCx, &A) -> bool + 'static,
msg_fn: impl Fn(bool) -> M + 'static,
) -> Self {
MenuToggle::new(label, state_fn).with_msg(msg_fn)
}
}
}