1use super::label::Label;
2use super::{Align, Bus, Event, Hub, Id, RenderQueue, View, ID_FEEDER};
3use crate::color::{TEXT_INVERTED_HARD, TEXT_NORMAL};
4use crate::context::Context;
5use crate::framebuffer::Framebuffer;
6use crate::geom::Rectangle;
7use crate::input::{DeviceEvent, FingerStatus};
8
9pub struct ActionLabel {
16 id: Id,
17 rect: Rectangle,
18 children: Vec<Box<dyn View>>,
19 active: bool,
20}
21
22impl ActionLabel {
23 pub fn new(rect: Rectangle, text: String, align: Align) -> ActionLabel {
24 let label = Label::new(rect, text, align);
25
26 ActionLabel {
27 id: ID_FEEDER.next(),
28 rect,
29 children: vec![Box::new(label)],
30 active: false,
31 }
32 }
33
34 pub fn event(mut self, event: Option<Event>) -> ActionLabel {
36 if let Some(label) = self.children[0].downcast_mut::<Label>() {
37 label.set_event(event);
38 }
39 self
40 }
41
42 pub fn set_event(&mut self, event: Option<Event>) {
44 if let Some(label) = self.children[0].downcast_mut::<Label>() {
45 label.set_event(event);
46 }
47 }
48
49 pub fn hold_event(mut self, event: Option<Event>) -> ActionLabel {
51 if let Some(label) = self.children[0].downcast_mut::<Label>() {
52 label.set_hold_event(event);
53 }
54 self
55 }
56
57 pub fn set_hold_event(&mut self, event: Option<Event>) {
59 if let Some(label) = self.children[0].downcast_mut::<Label>() {
60 label.set_hold_event(event);
61 }
62 }
63
64 pub fn update(&mut self, text: &str, rq: &mut RenderQueue) {
66 if let Some(label) = self.children[0].downcast_mut::<Label>() {
67 label.update(text, rq);
68 }
69 }
70
71 pub fn value(&self) -> String {
73 if let Some(label) = self.children[0].downcast_ref::<Label>() {
74 label.text().to_string()
75 } else {
76 String::new()
77 }
78 }
79
80 fn update_label_scheme(&mut self, rq: &mut RenderQueue) {
82 let scheme = if self.active {
83 TEXT_INVERTED_HARD
84 } else {
85 TEXT_NORMAL
86 };
87
88 if let Some(label) = self.children[0].downcast_mut::<Label>() {
89 label.set_scheme(scheme, rq);
90 }
91 }
92}
93
94impl View for ActionLabel {
95 #[cfg_attr(feature = "tracing", tracing::instrument(skip(self, _hub, _bus, rq, _context), fields(event = ?evt), ret(level=tracing::Level::TRACE)))]
108 fn handle_event(
109 &mut self,
110 evt: &Event,
111 _hub: &Hub,
112 _bus: &mut Bus,
113 rq: &mut RenderQueue,
114 _context: &mut Context,
115 ) -> bool {
116 match *evt {
117 Event::Device(DeviceEvent::Finger {
118 status, position, ..
119 }) => match status {
120 FingerStatus::Down if self.rect.includes(position) => {
121 self.active = true;
122 self.update_label_scheme(rq);
123 true
124 }
125 FingerStatus::Up if self.active => {
126 self.active = false;
127 self.update_label_scheme(rq);
128 true
129 }
130 _ => false,
131 },
132 _ => false,
133 }
134 }
135
136 #[cfg_attr(feature = "tracing", tracing::instrument(skip(self, _fb, _fonts), fields(rect = ?_rect)))]
137 fn render(&self, _fb: &mut dyn Framebuffer, _rect: Rectangle, _fonts: &mut crate::font::Fonts) {
138 }
139
140 fn rect(&self) -> &Rectangle {
141 &self.rect
142 }
143
144 fn rect_mut(&mut self) -> &mut Rectangle {
145 &mut self.rect
146 }
147
148 fn children(&self) -> &Vec<Box<dyn View>> {
149 &self.children
150 }
151
152 fn children_mut(&mut self) -> &mut Vec<Box<dyn View>> {
153 &mut self.children
154 }
155
156 fn id(&self) -> Id {
157 self.id
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164 use crate::context::test_helpers::create_test_context;
165 use crate::geom::Point;
166 use std::collections::VecDeque;
167 use std::sync::mpsc::channel;
168
169 #[test]
170 fn test_new_creates_with_label_child() {
171 let rect = rect![0, 0, 200, 50];
172 let action_label = ActionLabel::new(rect, "Test".to_string(), Align::Right(10));
173
174 assert_eq!(action_label.children.len(), 1);
175 assert!(!action_label.active);
176 }
177
178 #[test]
179 fn test_finger_down_activates() {
180 let rect = rect![0, 0, 200, 50];
181 let mut action_label = ActionLabel::new(rect, "Test".to_string(), Align::Right(10));
182 let (hub, _receiver) = channel();
183 let mut bus = VecDeque::new();
184 let mut rq = RenderQueue::new();
185 let mut context = create_test_context();
186
187 assert!(!action_label.active);
188
189 let point = Point::new(100, 25);
190 let event = Event::Device(DeviceEvent::Finger {
191 status: FingerStatus::Down,
192 position: point,
193 id: 0,
194 time: 0.0,
195 });
196 let handled = action_label.handle_event(&event, &hub, &mut bus, &mut rq, &mut context);
197
198 assert!(handled);
199 assert!(action_label.active);
200 assert!(!rq.is_empty());
201 }
202
203 #[test]
204 fn test_finger_up_deactivates() {
205 let rect = rect![0, 0, 200, 50];
206 let mut action_label = ActionLabel::new(rect, "Test".to_string(), Align::Right(10));
207
208 let (hub, _receiver) = channel();
209 let mut bus = VecDeque::new();
210 let mut rq = RenderQueue::new();
211
212 action_label.active = true;
214 if let Some(label) = action_label.children[0].downcast_mut::<Label>() {
215 label.set_scheme(TEXT_INVERTED_HARD, &mut rq);
216 }
217 rq.clear();
218 let mut context = create_test_context();
219
220 let point = Point::new(100, 25);
221 let event = Event::Device(DeviceEvent::Finger {
222 status: FingerStatus::Up,
223 position: point,
224 id: 0,
225 time: 0.0,
226 });
227 let handled = action_label.handle_event(&event, &hub, &mut bus, &mut rq, &mut context);
228
229 assert!(handled);
230 assert!(!action_label.active);
231 assert!(!rq.is_empty());
232 }
233
234 #[test]
235 fn test_finger_down_outside_rect_ignored() {
236 let rect = rect![0, 0, 200, 50];
237 let mut action_label = ActionLabel::new(rect, "Test".to_string(), Align::Right(10));
238 let (hub, _receiver) = channel();
239 let mut bus = VecDeque::new();
240 let mut rq = RenderQueue::new();
241 let mut context = create_test_context();
242
243 let point = Point::new(300, 100);
244 let event = Event::Device(DeviceEvent::Finger {
245 status: FingerStatus::Down,
246 position: point,
247 id: 0,
248 time: 0.0,
249 });
250 let handled = action_label.handle_event(&event, &hub, &mut bus, &mut rq, &mut context);
251
252 assert!(!handled);
253 assert!(!action_label.active);
254 }
255
256 #[test]
257 fn test_update_changes_label_text() {
258 let rect = rect![0, 0, 200, 50];
259 let mut action_label = ActionLabel::new(rect, "Initial".to_string(), Align::Right(10));
260 let mut rq = RenderQueue::new();
261
262 action_label.update("Updated", &mut rq);
263
264 assert!(!rq.is_empty());
265 if let Some(label) = action_label.children[0].downcast_ref::<Label>() {
266 assert_eq!(label.rect(), &rect);
267 }
268 }
269
270 #[test]
271 fn test_event_is_emitted_on_tap() {
272 let rect = rect![0, 0, 200, 50];
273 let action_label =
274 ActionLabel::new(rect, "Test".to_string(), Align::Right(10)).event(Some(Event::Back));
275
276 let (hub, _receiver) = channel();
277 let mut bus = VecDeque::new();
278 let mut rq = RenderQueue::new();
279 let mut context = create_test_context();
280
281 let point = Point::new(100, 25);
282 let tap_event = Event::Gesture(crate::gesture::GestureEvent::Tap(point));
283
284 let mut boxed: Box<dyn View> = Box::new(action_label);
285 crate::view::handle_event(
286 boxed.as_mut(),
287 &tap_event,
288 &hub,
289 &mut bus,
290 &mut rq,
291 &mut context,
292 );
293
294 assert_eq!(bus.len(), 1);
295 assert!(matches!(bus.pop_front(), Some(Event::Back)));
296 }
297
298 #[test]
299 fn test_set_event_updates_label() {
300 let rect = rect![0, 0, 200, 50];
301 let mut action_label = ActionLabel::new(rect, "Test".to_string(), Align::Right(10));
302
303 action_label.set_event(Some(Event::Back));
304
305 let (hub, _receiver) = channel();
306 let mut bus = VecDeque::new();
307 let mut rq = RenderQueue::new();
308 let mut context = create_test_context();
309
310 let point = Point::new(100, 25);
311 let tap_event = Event::Gesture(crate::gesture::GestureEvent::Tap(point));
312
313 let mut boxed: Box<dyn View> = Box::new(action_label);
314 crate::view::handle_event(
315 boxed.as_mut(),
316 &tap_event,
317 &hub,
318 &mut bus,
319 &mut rq,
320 &mut context,
321 );
322
323 assert_eq!(bus.len(), 1);
324 assert!(matches!(bus.pop_front(), Some(Event::Back)));
325 }
326}