kas_widgets/menu/
menu_entry.rs1use super::{Menu, SubItems};
9use crate::{AccessLabel, CheckBox};
10use kas::prelude::*;
11use kas::theme::{FrameStyle, TextClass};
12use std::fmt::Debug;
13
14impl_scope! {
15 #[derive(Clone, Debug, Default)]
22 #[widget {
23 layout = self.label;
24 navigable = true;
25 }]
26 pub struct MenuEntry<M: Clone + Debug + 'static> {
27 core: widget_core!(),
28 #[widget]
29 label: AccessLabel,
30 msg: M,
31 }
32
33 impl Layout for Self {
34 fn find_id(&mut self, coord: Coord) -> Option<Id> {
35 self.rect().contains(coord).then(|| self.id())
36 }
37
38 fn draw(&mut self, mut draw: DrawCx) {
39 draw.frame(self.rect(), FrameStyle::MenuEntry, Default::default());
40 self.label.draw(draw);
41 }
42 }
43
44 impl Self {
45 pub fn new_msg<S: Into<AccessString>>(label: S, msg: M) -> Self {
51 MenuEntry {
52 core: Default::default(),
53 label: AccessLabel::new(label).with_class(TextClass::MenuLabel),
54 msg,
55 }
56 }
57
58 pub fn set_msg(&mut self, msg: M) {
60 self.msg = msg;
61 }
62 }
63
64 impl HasStr for Self {
65 fn get_str(&self) -> &str {
66 self.label.get_str()
67 }
68 }
69
70 impl Events for Self {
71 type Data = ();
72
73 fn handle_event(&mut self, cx: &mut EventCx, _: &Self::Data, event: Event) -> IsUsed {
74 match event {
75 Event::Command(cmd, code) if cmd.is_activate() => {
76 cx.push(self.msg.clone());
77 cx.depress_with_key(self.id(), code);
78 Used
79 }
80 _ => Unused,
81 }
82 }
83
84 fn handle_messages(&mut self, cx: &mut EventCx, _: &Self::Data) {
85 if let Some(kas::messages::Activate(code)) = cx.try_pop() {
86 cx.push(self.msg.clone());
87 cx.depress_with_key(self.id(), code);
88 }
89 }
90 }
91
92 impl Menu for Self {
93 fn sub_items(&mut self) -> Option<SubItems> {
94 Some(SubItems {
95 label: Some(&mut self.label),
96 ..Default::default()
97 })
98 }
99 }
100
101 impl PartialEq<M> for Self where M: PartialEq {
102 #[inline]
103 fn eq(&self, rhs: &M) -> bool {
104 self.msg == *rhs
105 }
106 }
107}
108
109impl_scope! {
110 #[widget {
112 layout = row! [self.checkbox, self.label];
113 }]
114 pub struct MenuToggle<A> {
115 core: widget_core!(),
116 #[widget]
117 checkbox: CheckBox<A>,
118 #[widget(&())]
119 label: AccessLabel,
120 }
121
122 impl Layout for Self {
123 fn find_id(&mut self, coord: Coord) -> Option<Id> {
124 self.rect().contains(coord).then(|| self.checkbox.id())
125 }
126
127 fn draw(&mut self, mut draw: DrawCx) {
128 let mut draw = draw.re_id(self.checkbox.id());
129 draw.frame(self.rect(), FrameStyle::MenuEntry, Default::default());
130 self.layout_visitor().draw(draw);
131 }
132 }
133
134 impl Events for Self {
135 type Data = A;
136
137 fn handle_messages(&mut self, cx: &mut EventCx, data: &Self::Data) {
138 if let Some(kas::messages::Activate(code)) = cx.try_pop() {
139 self.checkbox.toggle(cx, data);
140 cx.depress_with_key(self.id(), code);
141 }
142 }
143 }
144
145 impl Menu for Self {
146 fn sub_items(&mut self) -> Option<SubItems> {
147 Some(SubItems {
148 label: Some(&mut self.label),
149 toggle: Some(&mut self.checkbox),
150 ..Default::default()
151 })
152 }
153 }
154
155 impl Self {
156 #[inline]
161 pub fn new(
162 label: impl Into<AccessString>,
163 state_fn: impl Fn(&ConfigCx, &A) -> bool + 'static,
164 ) -> Self {
165 MenuToggle {
166 core: Default::default(),
167 checkbox: CheckBox::new(state_fn),
168 label: AccessLabel::new(label).with_class(TextClass::MenuLabel),
169 }
170 }
171
172 #[inline]
174 #[must_use]
175 pub fn with<F>(self, f: F) -> Self
176 where
177 F: Fn(&mut EventCx, &A, bool) + 'static,
178 {
179 MenuToggle {
180 core: self.core,
181 checkbox: self.checkbox.with(f),
182 label: self.label,
183 }
184 }
185
186 #[inline]
188 #[must_use]
189 pub fn with_msg<M>(self, f: impl Fn(bool) -> M + 'static) -> Self
190 where
191 M: std::fmt::Debug + 'static,
192 {
193 self.with(move |cx, _, state| cx.push(f(state)))
194 }
195
196 #[inline]
202 pub fn new_msg<M: Debug + 'static>(
203 label: impl Into<AccessString>,
204 state_fn: impl Fn(&ConfigCx, &A) -> bool + 'static,
205 msg_fn: impl Fn(bool) -> M + 'static,
206 ) -> Self {
207 MenuToggle::new(label, state_fn).with_msg(msg_fn)
208 }
209 }
210}