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::context::test_helpers::create_test_context;
259 use std::sync::mpsc::channel;
260
261 fn create_test_keyboard() -> ToggleableKeyboard {
262 let parent_rect = rect![0, 0, 600, 800];
263 ToggleableKeyboard::new(parent_rect, false)
264 }
265
266 fn create_test_context_with_keyboard_data() -> Context {
267 let mut context = create_test_context();
268 context.load_keyboard_layouts();
269 context.load_dictionaries();
270 context
271 }
272
273 #[test]
274 fn test_new_creates_hidden_keyboard() {
275 let keyboard = create_test_keyboard();
276 assert!(!keyboard.is_visible());
277 assert_eq!(keyboard.children.len(), 0);
278 assert!(!keyboard.number_mode);
279 }
280
281 #[test]
282 fn test_new_with_number_mode() {
283 let parent_rect = rect![0, 0, 600, 800];
284 let keyboard = ToggleableKeyboard::new(parent_rect, true);
285 assert!(keyboard.number_mode);
286 }
287
288 #[test]
289 fn test_is_visible_initially_false() {
290 let keyboard = create_test_keyboard();
291 assert!(!keyboard.is_visible());
292 }
293
294 #[test]
295 fn test_set_number_mode() {
296 let mut keyboard = create_test_keyboard();
297 assert!(!keyboard.number_mode);
298
299 keyboard.set_number_mode(true);
300 assert!(keyboard.number_mode);
301
302 keyboard.set_number_mode(false);
303 assert!(!keyboard.number_mode);
304 }
305
306 #[test]
307 fn test_rect_defaults_to_empty() {
308 let keyboard = create_test_keyboard();
309 let rect = keyboard.rect();
310 assert_eq!(rect.min.x, 0);
311 assert_eq!(rect.min.y, 0);
312 assert_eq!(rect.max.x, 0);
313 assert_eq!(rect.max.y, 0);
314 }
315
316 #[test]
317 fn test_children_empty_when_hidden() {
318 let keyboard = create_test_keyboard();
319 assert!(keyboard.children().is_empty());
320 }
321
322 #[test]
323 fn test_parent_rect_stored_correctly() {
324 let parent_rect = rect![10, 20, 590, 780];
325 let keyboard = ToggleableKeyboard::new(parent_rect, false);
326 assert_eq!(keyboard.parent_rect, parent_rect);
327 }
328
329 #[test]
330 fn test_toggle_from_hidden_shows_keyboard() {
331 let mut keyboard = create_test_keyboard();
332 let (hub, _receiver) = channel();
333 let mut rq = RenderQueue::new();
334 let mut context = create_test_context_with_keyboard_data();
335
336 assert!(!keyboard.is_visible());
337 assert!(keyboard.children.is_empty());
338 assert!(rq.is_empty());
339
340 keyboard.toggle(&hub, &mut rq, &mut context);
341
342 assert!(keyboard.is_visible());
343 assert_eq!(keyboard.children.len(), 2);
344 assert_eq!(rq.len(), 1);
345 }
346
347 #[test]
348 fn test_toggle_from_visible_hides_keyboard() {
349 let mut keyboard = create_test_keyboard();
350 let (hub, receiver) = channel();
351 let mut rq = RenderQueue::new();
352 let mut context = create_test_context_with_keyboard_data();
353
354 keyboard.toggle(&hub, &mut rq, &mut context);
355 assert!(keyboard.is_visible());
356 assert_eq!(keyboard.children.len(), 2);
357
358 rq = RenderQueue::new();
359 keyboard.toggle(&hub, &mut rq, &mut context);
360
361 assert!(!keyboard.is_visible());
362 assert!(keyboard.children.is_empty());
363 assert_eq!(rq.len(), 1);
364 assert_eq!(context.kb_rect, Rectangle::default());
365
366 let focus_event = receiver.try_recv().unwrap();
367 assert!(matches!(focus_event, Event::Focus(None)));
368 }
369
370 #[test]
371 fn test_toggle_twice_returns_to_original_state() {
372 let mut keyboard = create_test_keyboard();
373 let (hub, _receiver) = channel();
374 let mut rq = RenderQueue::new();
375 let mut context = create_test_context_with_keyboard_data();
376
377 keyboard.toggle(&hub, &mut rq, &mut context);
378 keyboard.toggle(&hub, &mut rq, &mut context);
379
380 assert!(!keyboard.is_visible());
381 assert!(keyboard.children.is_empty());
382 }
383
384 #[test]
385 fn test_toggle_adds_render_data_each_time() {
386 let mut keyboard = create_test_keyboard();
387 let (hub, _receiver) = channel();
388 let mut context = create_test_context_with_keyboard_data();
389
390 let mut rq = RenderQueue::new();
391 keyboard.toggle(&hub, &mut rq, &mut context);
392 assert_eq!(rq.len(), 1);
393
394 let mut rq = RenderQueue::new();
395 keyboard.toggle(&hub, &mut rq, &mut context);
396 assert_eq!(rq.len(), 1);
397 }
398
399 #[test]
400 fn test_set_visible_true_shows_keyboard() {
401 let mut keyboard = create_test_keyboard();
402 let (hub, _receiver) = channel();
403 let mut rq = RenderQueue::new();
404 let mut context = create_test_context_with_keyboard_data();
405
406 assert!(!keyboard.is_visible());
407 assert!(keyboard.children.is_empty());
408
409 keyboard.set_visible(true, &hub, &mut rq, &mut context);
410
411 assert!(keyboard.is_visible());
412 assert_eq!(keyboard.children.len(), 2);
413 assert_eq!(rq.len(), 1);
414 }
415
416 #[test]
417 fn test_set_visible_false_hides_keyboard() {
418 let mut keyboard = create_test_keyboard();
419 let (hub, receiver) = channel();
420 let mut rq = RenderQueue::new();
421 let mut context = create_test_context_with_keyboard_data();
422
423 keyboard.set_visible(true, &hub, &mut rq, &mut context);
424 assert!(keyboard.is_visible());
425 assert_eq!(keyboard.children.len(), 2);
426
427 rq = RenderQueue::new();
428 keyboard.set_visible(false, &hub, &mut rq, &mut context);
429
430 assert!(!keyboard.is_visible());
431 assert!(keyboard.children.is_empty());
432 assert_eq!(rq.len(), 1);
433 assert_eq!(context.kb_rect, Rectangle::default());
434
435 let focus_event = receiver.try_recv().unwrap();
436 assert!(matches!(focus_event, Event::Focus(None)));
437 }
438
439 #[test]
440 fn test_set_visible_noop_when_already_visible() {
441 let mut keyboard = create_test_keyboard();
442 let (hub, _receiver) = channel();
443 let mut rq = RenderQueue::new();
444 let mut context = create_test_context_with_keyboard_data();
445
446 keyboard.set_visible(true, &hub, &mut rq, &mut context);
447 assert!(keyboard.is_visible());
448
449 rq = RenderQueue::new();
450 keyboard.set_visible(true, &hub, &mut rq, &mut context);
451
452 assert!(keyboard.is_visible());
453 assert!(rq.is_empty());
454 }
455
456 #[test]
457 fn test_set_visible_noop_when_already_hidden() {
458 let mut keyboard = create_test_keyboard();
459 let (hub, _receiver) = channel();
460 let mut rq = RenderQueue::new();
461 let mut context = create_test_context_with_keyboard_data();
462
463 assert!(!keyboard.is_visible());
464
465 keyboard.set_visible(false, &hub, &mut rq, &mut context);
466
467 assert!(!keyboard.is_visible());
468 assert!(rq.is_empty());
469 }
470}