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
14#[impl_self]
15mod MenuEntry {
16 #[derive(Clone, Debug, Default)]
27 #[widget]
28 #[layout(self.label)]
29 pub struct MenuEntry<M: Clone + Debug + 'static> {
30 core: widget_core!(),
31 #[widget]
32 label: AccessLabel,
33 msg: M,
34 }
35
36 impl Layout for Self {
37 fn draw(&self, mut draw: DrawCx) {
38 draw.frame(self.rect(), FrameStyle::MenuEntry, Default::default());
39 self.label.draw(draw.re());
40 }
41 }
42
43 impl Tile for Self {
44 fn navigable(&self) -> bool {
45 true
46 }
47
48 fn role(&self, cx: &mut dyn RoleCx) -> Role<'_> {
49 cx.set_label(self.label.id());
50 Role::Button
51 }
52
53 fn probe(&self, _: Coord) -> Id {
54 self.id()
55 }
56 }
57
58 impl Self {
59 pub fn new_msg<S: Into<AccessString>>(label: S, msg: M) -> Self {
65 MenuEntry {
66 core: Default::default(),
67 label: AccessLabel::new(label).with_class(TextClass::MenuLabel),
68 msg,
69 }
70 }
71
72 pub fn set_msg(&mut self, msg: M) {
74 self.msg = msg;
75 }
76
77 pub fn as_str(&self) -> &str {
79 self.label.as_str()
80 }
81 }
82
83 impl Events for Self {
84 type Data = ();
85
86 fn handle_event(&mut self, cx: &mut EventCx, _: &Self::Data, event: Event) -> IsUsed {
87 match event {
88 Event::Command(cmd, code) if cmd.is_activate() => {
89 cx.push(self.msg.clone());
90 cx.depress_with_key(&self, code);
91 Used
92 }
93 _ => Unused,
94 }
95 }
96
97 fn handle_messages(&mut self, cx: &mut EventCx, _: &Self::Data) {
98 if let Some(kas::messages::Activate(code)) = cx.try_pop() {
99 cx.push(self.msg.clone());
100 cx.depress_with_key(&self, code);
101 }
102 }
103 }
104
105 impl Menu for Self {
106 fn sub_items(&mut self) -> Option<SubItems<'_>> {
107 Some(SubItems {
108 label: Some(&mut self.label),
109 ..Default::default()
110 })
111 }
112 }
113
114 impl PartialEq<M> for Self
115 where
116 M: PartialEq,
117 {
118 #[inline]
119 fn eq(&self, rhs: &M) -> bool {
120 self.msg == *rhs
121 }
122 }
123}
124
125#[impl_self]
126mod MenuToggle {
127 #[widget]
133 #[layout(row! [self.checkbox, self.label])]
134 pub struct MenuToggle<A> {
135 core: widget_core!(),
136 #[widget]
137 checkbox: CheckBox<A>,
138 #[widget(&())]
139 label: AccessLabel,
140 }
141
142 impl Layout for Self {
143 fn draw(&self, mut draw: DrawCx) {
144 draw.set_id(self.checkbox.id());
145 draw.frame(self.rect(), FrameStyle::MenuEntry, Default::default());
146 kas::MacroDefinedLayout::draw(self, draw);
147 }
148 }
149
150 impl Tile for Self {
151 fn role_child_properties(&self, cx: &mut dyn RoleCx, index: usize) {
152 if index == widget_index!(self.checkbox) {
153 cx.set_label(self.label.id());
154 }
155 }
156
157 fn probe(&self, _: Coord) -> Id {
158 self.checkbox.id()
159 }
160 }
161
162 impl Events for Self {
163 type Data = A;
164
165 fn configure_recurse(&mut self, cx: &mut ConfigCx, data: &Self::Data) {
166 let id = self.make_child_id(widget_index!(self.checkbox));
167 if id.is_valid() {
168 cx.configure(self.checkbox.as_node(data), id);
169 }
170
171 let id = self.make_child_id(widget_index!(self.label));
172 if id.is_valid() {
173 cx.configure(self.label.as_node(&()), id);
174 self.label.set_target(self.checkbox.id());
175 }
176 }
177
178 fn handle_messages(&mut self, cx: &mut EventCx, data: &Self::Data) {
179 if let Some(kas::messages::Activate(code)) = cx.try_pop() {
180 self.checkbox.toggle(cx, data);
181 cx.depress_with_key(&self, code);
182 }
183 }
184 }
185
186 impl Menu for Self {
187 fn sub_items(&mut self) -> Option<SubItems<'_>> {
188 Some(SubItems {
189 label: Some(&mut self.label),
190 toggle: Some(&mut self.checkbox),
191 ..Default::default()
192 })
193 }
194 }
195
196 impl Self {
197 #[inline]
202 pub fn new(
203 label: impl Into<AccessString>,
204 state_fn: impl Fn(&ConfigCx, &A) -> bool + 'static,
205 ) -> Self {
206 MenuToggle {
207 core: Default::default(),
208 checkbox: CheckBox::new(state_fn),
209 label: AccessLabel::new(label).with_class(TextClass::MenuLabel),
210 }
211 }
212
213 #[inline]
215 #[must_use]
216 pub fn with<F>(self, f: F) -> Self
217 where
218 F: Fn(&mut EventCx, &A, bool) + 'static,
219 {
220 MenuToggle {
221 core: self.core,
222 checkbox: self.checkbox.with(f),
223 label: self.label,
224 }
225 }
226
227 #[inline]
229 #[must_use]
230 pub fn with_msg<M>(self, f: impl Fn(bool) -> M + 'static) -> Self
231 where
232 M: std::fmt::Debug + 'static,
233 {
234 self.with(move |cx, _, state| cx.push(f(state)))
235 }
236
237 #[inline]
243 pub fn new_msg<M: Debug + 'static>(
244 label: impl Into<AccessString>,
245 state_fn: impl Fn(&ConfigCx, &A) -> bool + 'static,
246 msg_fn: impl Fn(bool) -> M + 'static,
247 ) -> Self {
248 MenuToggle::new(label, state_fn).with_msg(msg_fn)
249 }
250 }
251}