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(Debug)]
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
54 impl Self {
55 pub fn new_msg<S: Into<AccessString>>(label: S, msg: M) -> Self {
61 MenuEntry {
62 core: Default::default(),
63 label: AccessLabel::new(label).with_class(TextClass::Label),
64 msg,
65 }
66 }
67
68 pub fn set_msg(&mut self, msg: M) {
70 self.msg = msg;
71 }
72
73 pub fn as_str(&self) -> &str {
75 self.label.as_str()
76 }
77 }
78
79 impl Events for Self {
80 type Data = ();
81
82 fn probe(&self, _: Coord) -> Id {
83 self.id()
84 }
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
158 impl Events for Self {
159 type Data = A;
160
161 fn probe(&self, _: Coord) -> Id {
162 self.checkbox.id()
163 }
164
165 fn post_configure(&mut self, _: &mut ConfigCx) {
166 self.label.set_target(self.checkbox.id());
167 }
168
169 fn handle_messages(&mut self, cx: &mut EventCx, data: &Self::Data) {
170 if let Some(kas::messages::Activate(code)) = cx.try_pop() {
171 self.checkbox.toggle(cx, data);
172 cx.depress_with_key(&self, code);
173 }
174 }
175 }
176
177 impl Menu for Self {
178 fn sub_items(&mut self) -> Option<SubItems<'_>> {
179 Some(SubItems {
180 label: Some(&mut self.label),
181 toggle: Some(&mut self.checkbox),
182 ..Default::default()
183 })
184 }
185 }
186
187 impl Self {
188 #[inline]
193 pub fn new(
194 label: impl Into<AccessString>,
195 state_fn: impl Fn(&ConfigCx, &A) -> bool + 'static,
196 ) -> Self {
197 MenuToggle {
198 core: Default::default(),
199 checkbox: CheckBox::new(state_fn),
200 label: AccessLabel::new(label).with_class(TextClass::Label),
201 }
202 }
203
204 #[inline]
206 #[must_use]
207 pub fn with<F>(self, f: F) -> Self
208 where
209 F: Fn(&mut EventCx, &A, bool) + 'static,
210 {
211 MenuToggle {
212 core: self.core,
213 checkbox: self.checkbox.with(f),
214 label: self.label,
215 }
216 }
217
218 #[inline]
220 #[must_use]
221 pub fn with_msg<M>(self, f: impl Fn(bool) -> M + 'static) -> Self
222 where
223 M: std::fmt::Debug + 'static,
224 {
225 self.with(move |cx, _, state| cx.push(f(state)))
226 }
227
228 #[inline]
234 pub fn new_msg<M: Debug + 'static>(
235 label: impl Into<AccessString>,
236 state_fn: impl Fn(&ConfigCx, &A) -> bool + 'static,
237 msg_fn: impl Fn(bool) -> M + 'static,
238 ) -> Self {
239 MenuToggle::new(label, state_fn).with_msg(msg_fn)
240 }
241 }
242}