1use crate::color::BLACK;
7use crate::context::Context;
8use crate::device::CURRENT_DEVICE;
9use crate::font::Fonts;
10use crate::framebuffer::{Framebuffer, UpdateMode};
11use crate::geom::{halves, Rectangle};
12use crate::unit::scale_by_dpi;
13use crate::view::filler::Filler;
14use crate::view::keyboard::Keyboard;
15use crate::view::{Bus, Event, Hub, Id, RenderData, RenderQueue, View, ID_FEEDER};
16use crate::view::{BIG_BAR_HEIGHT, SMALL_BAR_HEIGHT, THICKNESS_MEDIUM};
17
18pub struct ToggleableKeyboard {
37 id: Id,
38 rect: Rectangle,
39 children: Vec<Box<dyn View>>,
40 visible: bool,
41 parent_rect: Rectangle,
42 number_mode: bool,
43}
44
45impl ToggleableKeyboard {
46 pub fn new(parent_rect: Rectangle, number_mode: bool) -> Self {
60 ToggleableKeyboard {
61 id: ID_FEEDER.next(),
62 rect: Rectangle::default(),
63 children: Vec::new(),
64 visible: false,
65 parent_rect,
66 number_mode,
67 }
68 }
69
70 pub fn toggle(&mut self, hub: &Hub, rq: &mut RenderQueue, context: &mut Context) {
82 if self.visible {
83 self.hide(hub, rq, context);
84 } else {
85 self.show(rq, context);
86 }
87 }
88
89 pub fn set_visible(
102 &mut self,
103 visible: bool,
104 hub: &Hub,
105 rq: &mut RenderQueue,
106 context: &mut Context,
107 ) {
108 if self.visible == visible {
109 return;
110 }
111
112 if visible {
113 self.show(rq, context);
114 } else {
115 self.hide(hub, rq, context);
116 }
117 }
118
119 pub fn set_number_mode(&mut self, number_mode: bool) {
129 self.number_mode = number_mode;
130 }
131
132 pub fn is_visible(&self) -> bool {
138 self.visible
139 }
140
141 fn show(&mut self, rq: &mut RenderQueue, context: &mut Context) {
146 let dpi = CURRENT_DEVICE.dpi;
147 let (small_height, big_height) = (
148 scale_by_dpi(SMALL_BAR_HEIGHT, dpi) as i32,
149 scale_by_dpi(BIG_BAR_HEIGHT, dpi) as i32,
150 );
151 let thickness = scale_by_dpi(THICKNESS_MEDIUM, dpi) as i32;
152 let (_small_thickness, big_thickness) = halves(thickness);
153
154 let separator = Filler::new(
155 rect![
156 self.parent_rect.min.x,
157 self.parent_rect.max.y - (small_height + 3 * big_height),
158 self.parent_rect.max.x,
159 self.parent_rect.max.y - (small_height + 3 * big_height) + thickness
160 ],
161 BLACK,
162 );
163 self.children.push(Box::new(separator) as Box<dyn View>);
164
165 let mut kb_rect = rect![
166 self.parent_rect.min.x,
167 self.parent_rect.max.y - (small_height + 3 * big_height) + big_thickness,
168 self.parent_rect.max.x,
169 self.parent_rect.max.y - small_height - big_thickness
170 ];
171
172 let keyboard = Keyboard::new(&mut kb_rect, self.number_mode, context);
173 self.children.push(Box::new(keyboard) as Box<dyn View>);
174
175 self.rect = kb_rect;
176 self.rect.absorb(self.children[0].rect());
177
178 self.visible = true;
179
180 rq.add(RenderData::new(self.id, self.rect, UpdateMode::Gui));
181 }
182
183 fn hide(&mut self, hub: &Hub, rq: &mut RenderQueue, context: &mut Context) {
188 let rect = self.rect;
189
190 self.children.clear();
191
192 context.kb_rect = Rectangle::default();
193 self.rect = Rectangle::default();
194 self.visible = false;
195
196 hub.send(Event::Focus(None)).ok();
197 rq.add(RenderData::expose(rect, UpdateMode::Gui));
198 }
199}
200
201impl View for ToggleableKeyboard {
202 #[cfg_attr(feature = "otel", tracing::instrument(skip(self, hub, bus, rq, context), fields(event = ?evt), ret(level=tracing::Level::TRACE)))]
203 fn handle_event(
204 &mut self,
205 evt: &Event,
206 hub: &Hub,
207 bus: &mut Bus,
208 rq: &mut RenderQueue,
209 context: &mut Context,
210 ) -> bool {
211 if !self.visible {
212 return false;
213 }
214
215 for child in &mut self.children {
216 if child.handle_event(evt, hub, bus, rq, context) {
217 return true;
218 }
219 }
220
221 false
222 }
223 #[cfg_attr(feature = "otel", tracing::instrument(skip(self, fb, fonts), fields(rect = ?rect)))]
224 fn render(&self, fb: &mut dyn Framebuffer, rect: Rectangle, fonts: &mut Fonts) {
225 if !self.visible {
226 return;
227 }
228
229 for child in &self.children {
230 child.render(fb, rect, fonts);
231 }
232 }
233
234 fn rect(&self) -> &Rectangle {
235 &self.rect
236 }
237
238 fn rect_mut(&mut self) -> &mut Rectangle {
239 &mut self.rect
240 }
241
242 fn children(&self) -> &Vec<Box<dyn View>> {
243 &self.children
244 }
245
246 fn children_mut(&mut self) -> &mut Vec<Box<dyn View>> {
247 &mut self.children
248 }
249
250 fn id(&self) -> Id {
251 self.id
252 }
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258 use crate::battery::{Battery, FakeBattery};
259 use crate::framebuffer::Pixmap;
260 use crate::frontlight::{Frontlight, LightLevels};
261 use crate::library::Library;
262 use crate::lightsensor::LightSensor;
263 use crate::settings::{LibraryMode, Settings};
264 use std::env;
265 use std::path::Path;
266 use std::sync::mpsc::channel;
267
268 fn create_test_keyboard() -> ToggleableKeyboard {
269 let parent_rect = rect![0, 0, 600, 800];
270 ToggleableKeyboard::new(parent_rect, false)
271 }
272
273 fn create_test_context() -> Context {
274 let fb = Box::new(Pixmap::new(600, 800, 1)) as Box<dyn Framebuffer>;
275 let battery = Box::new(FakeBattery::new()) as Box<dyn Battery>;
276 let frontlight = Box::new(LightLevels::default()) as Box<dyn Frontlight>;
277 let lightsensor = Box::new(0u16) as Box<dyn LightSensor>;
278 let settings = Settings::default();
279 let library = Library::new(Path::new("."), LibraryMode::Database).unwrap_or_else(|_| {
280 Library::new(Path::new("/tmp"), LibraryMode::Database).expect(
281 "Failed to create test library. \
282 Ensure /tmp directory exists and is writable.",
283 )
284 });
285 let fonts = Fonts::load_from(
286 Path::new(
287 &env::var("TEST_ROOT_DIR").expect("TEST_ROOT_DIR must be set for this test."),
288 )
289 .to_path_buf(),
290 )
291 .expect(
292 "Failed to load fonts. Tests require font files to be present. \
293 Run tests from the project root directory.",
294 );
295
296 let mut ctx = Context::new(
297 fb,
298 None,
299 library,
300 settings,
301 fonts,
302 battery,
303 frontlight,
304 lightsensor,
305 );
306 ctx.load_keyboard_layouts();
307 ctx.load_dictionaries();
308
309 ctx
310 }
311
312 #[test]
313 fn test_new_creates_hidden_keyboard() {
314 let keyboard = create_test_keyboard();
315 assert!(!keyboard.is_visible());
316 assert_eq!(keyboard.children.len(), 0);
317 assert!(!keyboard.number_mode);
318 }
319
320 #[test]
321 fn test_new_with_number_mode() {
322 let parent_rect = rect![0, 0, 600, 800];
323 let keyboard = ToggleableKeyboard::new(parent_rect, true);
324 assert!(keyboard.number_mode);
325 }
326
327 #[test]
328 fn test_is_visible_initially_false() {
329 let keyboard = create_test_keyboard();
330 assert!(!keyboard.is_visible());
331 }
332
333 #[test]
334 fn test_set_number_mode() {
335 let mut keyboard = create_test_keyboard();
336 assert!(!keyboard.number_mode);
337
338 keyboard.set_number_mode(true);
339 assert!(keyboard.number_mode);
340
341 keyboard.set_number_mode(false);
342 assert!(!keyboard.number_mode);
343 }
344
345 #[test]
346 fn test_rect_defaults_to_empty() {
347 let keyboard = create_test_keyboard();
348 let rect = keyboard.rect();
349 assert_eq!(rect.min.x, 0);
350 assert_eq!(rect.min.y, 0);
351 assert_eq!(rect.max.x, 0);
352 assert_eq!(rect.max.y, 0);
353 }
354
355 #[test]
356 fn test_children_empty_when_hidden() {
357 let keyboard = create_test_keyboard();
358 assert!(keyboard.children().is_empty());
359 }
360
361 #[test]
362 fn test_parent_rect_stored_correctly() {
363 let parent_rect = rect![10, 20, 590, 780];
364 let keyboard = ToggleableKeyboard::new(parent_rect, false);
365 assert_eq!(keyboard.parent_rect, parent_rect);
366 }
367
368 #[test]
369 fn test_toggle_from_hidden_shows_keyboard() {
370 let mut keyboard = create_test_keyboard();
371 let (hub, _receiver) = channel();
372 let mut rq = RenderQueue::new();
373 let mut context = create_test_context();
374
375 assert!(!keyboard.is_visible());
376 assert!(keyboard.children.is_empty());
377 assert!(rq.is_empty());
378
379 keyboard.toggle(&hub, &mut rq, &mut context);
380
381 assert!(keyboard.is_visible());
382 assert_eq!(keyboard.children.len(), 2);
383 assert_eq!(rq.len(), 1);
384 }
385
386 #[test]
387 fn test_toggle_from_visible_hides_keyboard() {
388 let mut keyboard = create_test_keyboard();
389 let (hub, receiver) = channel();
390 let mut rq = RenderQueue::new();
391 let mut context = create_test_context();
392
393 keyboard.toggle(&hub, &mut rq, &mut context);
394 assert!(keyboard.is_visible());
395 assert_eq!(keyboard.children.len(), 2);
396
397 rq = RenderQueue::new();
398 keyboard.toggle(&hub, &mut rq, &mut context);
399
400 assert!(!keyboard.is_visible());
401 assert!(keyboard.children.is_empty());
402 assert_eq!(rq.len(), 1);
403 assert_eq!(context.kb_rect, Rectangle::default());
404
405 let focus_event = receiver.try_recv().unwrap();
406 assert!(matches!(focus_event, Event::Focus(None)));
407 }
408
409 #[test]
410 fn test_toggle_twice_returns_to_original_state() {
411 let mut keyboard = create_test_keyboard();
412 let (hub, _receiver) = channel();
413 let mut rq = RenderQueue::new();
414 let mut context = create_test_context();
415
416 keyboard.toggle(&hub, &mut rq, &mut context);
417 keyboard.toggle(&hub, &mut rq, &mut context);
418
419 assert!(!keyboard.is_visible());
420 assert!(keyboard.children.is_empty());
421 }
422
423 #[test]
424 fn test_toggle_adds_render_data_each_time() {
425 let mut keyboard = create_test_keyboard();
426 let (hub, _receiver) = channel();
427 let mut context = create_test_context();
428
429 let mut rq = RenderQueue::new();
430 keyboard.toggle(&hub, &mut rq, &mut context);
431 assert_eq!(rq.len(), 1);
432
433 let mut rq = RenderQueue::new();
434 keyboard.toggle(&hub, &mut rq, &mut context);
435 assert_eq!(rq.len(), 1);
436 }
437
438 #[test]
439 fn test_set_visible_true_shows_keyboard() {
440 let mut keyboard = create_test_keyboard();
441 let (hub, _receiver) = channel();
442 let mut rq = RenderQueue::new();
443 let mut context = create_test_context();
444
445 assert!(!keyboard.is_visible());
446 assert!(keyboard.children.is_empty());
447
448 keyboard.set_visible(true, &hub, &mut rq, &mut context);
449
450 assert!(keyboard.is_visible());
451 assert_eq!(keyboard.children.len(), 2);
452 assert_eq!(rq.len(), 1);
453 }
454
455 #[test]
456 fn test_set_visible_false_hides_keyboard() {
457 let mut keyboard = create_test_keyboard();
458 let (hub, receiver) = channel();
459 let mut rq = RenderQueue::new();
460 let mut context = create_test_context();
461
462 keyboard.set_visible(true, &hub, &mut rq, &mut context);
463 assert!(keyboard.is_visible());
464 assert_eq!(keyboard.children.len(), 2);
465
466 rq = RenderQueue::new();
467 keyboard.set_visible(false, &hub, &mut rq, &mut context);
468
469 assert!(!keyboard.is_visible());
470 assert!(keyboard.children.is_empty());
471 assert_eq!(rq.len(), 1);
472 assert_eq!(context.kb_rect, Rectangle::default());
473
474 let focus_event = receiver.try_recv().unwrap();
475 assert!(matches!(focus_event, Event::Focus(None)));
476 }
477
478 #[test]
479 fn test_set_visible_noop_when_already_visible() {
480 let mut keyboard = create_test_keyboard();
481 let (hub, _receiver) = channel();
482 let mut rq = RenderQueue::new();
483 let mut context = create_test_context();
484
485 keyboard.set_visible(true, &hub, &mut rq, &mut context);
486 assert!(keyboard.is_visible());
487
488 rq = RenderQueue::new();
489 keyboard.set_visible(true, &hub, &mut rq, &mut context);
490
491 assert!(keyboard.is_visible());
492 assert!(rq.is_empty());
493 }
494
495 #[test]
496 fn test_set_visible_noop_when_already_hidden() {
497 let mut keyboard = create_test_keyboard();
498 let (hub, _receiver) = channel();
499 let mut rq = RenderQueue::new();
500 let mut context = create_test_context();
501
502 assert!(!keyboard.is_visible());
503
504 keyboard.set_visible(false, &hub, &mut rq, &mut context);
505
506 assert!(!keyboard.is_visible());
507 assert!(rq.is_empty());
508 }
509}