cadmus_core/view/
keyboard.rs

1use super::key::{Key, KeyKind};
2use super::BIG_BAR_HEIGHT;
3use super::{
4    Bus, EntryId, Event, Hub, Id, KeyboardEvent, RenderData, RenderQueue, TextKind, View, ID_FEEDER,
5};
6use crate::color::KEYBOARD_BG;
7use crate::context::Context;
8use crate::device::CURRENT_DEVICE;
9use crate::font::Fonts;
10use crate::framebuffer::{Framebuffer, UpdateMode};
11use crate::geom::Rectangle;
12use crate::gesture::GestureEvent;
13use crate::input::DeviceEvent;
14use crate::unit::scale_by_dpi;
15use fxhash::FxHashMap;
16use lazy_static::lazy_static;
17use serde::Deserialize;
18
19const PADDING_RATIO: f32 = 0.06;
20
21#[derive(Debug, Clone, Deserialize)]
22#[serde(rename_all = "camelCase")]
23pub struct Layout {
24    pub name: String,
25    pub outputs: [Vec<Vec<char>>; 4],
26    pub keys: Vec<Vec<KeyKind>>,
27    pub widths: Vec<Vec<f32>>,
28}
29
30#[derive(Default, Debug)]
31pub struct State {
32    shift: u8,
33    alternate: u8,
34    combine: bool,
35}
36
37pub struct Keyboard {
38    id: Id,
39    rect: Rectangle,
40    children: Vec<Box<dyn View>>,
41    layout: Layout,
42    state: State,
43    combine_buffer: String,
44}
45
46impl Keyboard {
47    pub fn new(rect: &mut Rectangle, number: bool, context: &mut Context) -> Keyboard {
48        let id = ID_FEEDER.next();
49        let mut children = Vec::new();
50        let dpi = CURRENT_DEVICE.dpi;
51
52        let layout = context.keyboard_layouts[&context.settings.keyboard_layout].clone();
53
54        let mut state = State::default();
55
56        if number {
57            state.alternate = 2;
58        }
59
60        let mut level = 0;
61
62        if state.shift > 0 {
63            level += 1;
64        }
65
66        if state.alternate > 0 {
67            level += 2;
68        }
69
70        let max_width = layout
71            .widths
72            .iter()
73            .map(|row| (row.len() + 1) as f32 * PADDING_RATIO + row.iter().sum::<f32>())
74            .max_by(|a, b| a.partial_cmp(&b).expect("Found NaNs"))
75            .expect("Missing row widths");
76
77        let kh_1 = (rect.width() as f32) / max_width;
78        let rows_count = layout.keys.len();
79        let kh_2 =
80            (rect.height() as f32) / (rows_count as f32 + PADDING_RATIO * (rows_count + 1) as f32);
81        let key_height = kh_1.min(kh_2);
82        let padding = PADDING_RATIO * key_height;
83
84        let rows_height = key_height * rows_count as f32 + padding * (rows_count + 1) as f32;
85        let big_height = scale_by_dpi(BIG_BAR_HEIGHT, dpi) as i32;
86        let height_gap = (rect.height() - rows_height.round() as u32) / big_height as u32;
87        rect.min.y += height_gap as i32 * big_height;
88        context.kb_rect = *rect;
89
90        let start_y = rect.min.y as f32 + padding + (rect.height() as f32 - rows_height) / 2.0;
91
92        for (i, row) in layout.keys.iter().enumerate() {
93            let y = start_y + i as f32 * (padding + key_height);
94            let row_width = (layout.widths[i].len() + 1) as f32 * padding
95                + layout.widths[i].iter().sum::<f32>() * key_height;
96            let start_x = rect.min.x as f32 + padding + (rect.width() as f32 - row_width) / 2.0;
97            let mut dx = 0.0;
98            let mut dj = 0;
99
100            for (j, kind) in row.iter().enumerate() {
101                let key_width = layout.widths[i][j] * key_height;
102                let x = start_x + dx;
103                dx += key_width + padding;
104                let key_rect = rect![
105                    x.round() as i32,
106                    y.round() as i32,
107                    (x + key_width).round() as i32,
108                    (y + key_height).round() as i32
109                ];
110                let kind = match kind {
111                    KeyKind::Output(c) if *c != ' ' => {
112                        KeyKind::Output(layout.outputs[level][i][j - dj])
113                    }
114                    _ => {
115                        dj = j + 1;
116                        *kind
117                    }
118                };
119                let mut key = Key::new(key_rect, kind);
120                if number && kind == KeyKind::Alternate {
121                    key.lock();
122                }
123                children.push(Box::new(key) as Box<dyn View>);
124            }
125        }
126
127        Keyboard {
128            id,
129            rect: *rect,
130            children,
131            layout,
132            state,
133            combine_buffer: String::new(),
134        }
135    }
136
137    fn update(&mut self, rq: &mut RenderQueue) {
138        let mut level = 0;
139
140        if self.state.shift > 0 {
141            level += 1;
142        }
143
144        if self.state.alternate > 0 {
145            level += 2;
146        }
147
148        let mut index = 0;
149
150        for (i, row) in self.layout.keys.iter().enumerate() {
151            let mut dj = 0;
152
153            for (j, kind) in row.iter().enumerate() {
154                if kind.is_variable_output() {
155                    if let Some(child) = self.children[index].downcast_mut::<Key>() {
156                        let ch = self.layout.outputs[level][i][j - dj];
157                        child.update(KeyKind::Output(ch), rq);
158                    }
159                } else {
160                    dj = j + 1;
161                }
162                index += 1;
163            }
164        }
165    }
166
167    fn release_modifiers(&mut self, rq: &mut RenderQueue) {
168        if self.state.shift != 1 && self.state.alternate != 1 {
169            return;
170        }
171
172        if self.state.shift == 1 {
173            self.state.shift = 0;
174            for child in self.children_mut() {
175                if let Some(key) = child.downcast_mut::<Key>() {
176                    if *key.kind() == KeyKind::Shift {
177                        key.release(rq);
178                        break;
179                    }
180                }
181            }
182        }
183
184        if self.state.alternate == 1 {
185            self.state.alternate = 0;
186            for child in self.children_mut() {
187                if let Some(key) = child.downcast_mut::<Key>() {
188                    if *key.kind() == KeyKind::Alternate {
189                        key.release(rq);
190                        break;
191                    }
192                }
193            }
194        }
195
196        self.update(rq);
197    }
198
199    fn release_combine(&mut self, rq: &mut RenderQueue) {
200        self.state.combine = false;
201        self.combine_buffer.clear();
202        for child in self.children_mut() {
203            if let Some(key) = child.downcast_mut::<Key>() {
204                if *key.kind() == KeyKind::Combine {
205                    key.release(rq);
206                    break;
207                }
208            }
209        }
210    }
211}
212
213impl View for Keyboard {
214    #[cfg_attr(feature = "otel", tracing::instrument(skip(self, hub, _bus, rq, context), fields(event = ?evt), ret(level=tracing::Level::TRACE)))]
215    fn handle_event(
216        &mut self,
217        evt: &Event,
218        hub: &Hub,
219        _bus: &mut Bus,
220        rq: &mut RenderQueue,
221        context: &mut Context,
222    ) -> bool {
223        match *evt {
224            Event::Key(k) => {
225                match k {
226                    KeyKind::Output(ch) => {
227                        if self.state.combine {
228                            self.combine_buffer.push(ch);
229                            hub.send(Event::Keyboard(KeyboardEvent::Partial(ch))).ok();
230                            if self.combine_buffer.len() > 1 {
231                                if let Some(&ch) =
232                                    DEFAULT_COMBINATIONS.get(&self.combine_buffer[..])
233                                {
234                                    hub.send(Event::Keyboard(KeyboardEvent::Append(ch))).ok();
235                                }
236                                self.release_combine(rq);
237                            }
238                        } else {
239                            hub.send(Event::Keyboard(KeyboardEvent::Append(ch))).ok();
240                        }
241                        if ch != ' ' {
242                            self.release_modifiers(rq);
243                        }
244                    }
245                    KeyKind::Shift => {
246                        self.state.shift = (self.state.shift + 1) % 3;
247                        if self.state.shift != 2 {
248                            self.update(rq);
249                        }
250                    }
251                    KeyKind::Alternate => {
252                        self.state.alternate = (self.state.alternate + 1) % 3;
253                        if self.state.alternate != 2 {
254                            self.update(rq);
255                        }
256                    }
257                    KeyKind::Delete(dir) => {
258                        hub.send(Event::Keyboard(KeyboardEvent::Delete {
259                            target: TextKind::Char,
260                            dir,
261                        }))
262                        .ok();
263                    }
264                    KeyKind::Move(dir) => {
265                        hub.send(Event::Keyboard(KeyboardEvent::Move {
266                            target: TextKind::Char,
267                            dir,
268                        }))
269                        .ok();
270                    }
271                    KeyKind::Combine => self.state.combine = !self.state.combine,
272                    KeyKind::Return => {
273                        self.release_combine(rq);
274                        hub.send(Event::Keyboard(KeyboardEvent::Submit)).ok();
275                    }
276                };
277                true
278            }
279            Event::Select(EntryId::SetKeyboardLayout(ref name)) => {
280                if *name != context.settings.keyboard_layout {
281                    context.settings.keyboard_layout = name.to_string();
282                    // FIXME: the keyboard's height might change, in which case,
283                    // we shall notify the root view.
284                    *self = Keyboard::new(&mut self.rect, self.state.alternate == 2, context);
285                    rq.add(RenderData::new(self.id, self.rect, UpdateMode::Gui));
286                }
287                true
288            }
289            Event::Gesture(GestureEvent::Tap(center))
290            | Event::Gesture(GestureEvent::HoldFingerShort(center, ..))
291                if self.rect.includes(center) =>
292            {
293                true
294            }
295            Event::Gesture(GestureEvent::Swipe { start, .. }) if self.rect.includes(start) => true,
296            Event::Device(DeviceEvent::Finger { position, .. }) if self.rect.includes(position) => {
297                true
298            }
299            _ => false,
300        }
301    }
302
303    fn might_skip(&self, evt: &Event) -> bool {
304        !matches!(
305            *evt,
306            Event::Key(..)
307                | Event::Gesture(..)
308                | Event::Device(DeviceEvent::Finger { .. })
309                | Event::Select(..)
310        )
311    }
312
313    #[cfg_attr(feature = "otel", tracing::instrument(skip(self, fb, _fonts), fields(rect = ?rect)))]
314    fn render(&self, fb: &mut dyn Framebuffer, rect: Rectangle, _fonts: &mut Fonts) {
315        for child in &self.children {
316            if *child.rect() == rect {
317                return;
318            }
319        }
320
321        if let Some(region) = rect.intersection(&self.rect) {
322            fb.draw_rectangle(&region, KEYBOARD_BG);
323        }
324    }
325
326    fn render_rect(&self, rect: &Rectangle) -> Rectangle {
327        rect.intersection(&self.rect).unwrap_or(self.rect)
328    }
329
330    fn resize(
331        &mut self,
332        mut rect: Rectangle,
333        hub: &Hub,
334        rq: &mut RenderQueue,
335        context: &mut Context,
336    ) {
337        let dpi = CURRENT_DEVICE.dpi;
338        let max_width = self
339            .layout
340            .widths
341            .iter()
342            .map(|row| (row.len() + 1) as f32 * PADDING_RATIO + row.iter().sum::<f32>())
343            .max_by(|a, b| a.partial_cmp(b).expect("Found NaNs"))
344            .expect("Missing row widths");
345
346        let kh_1 = (rect.width() as f32) / max_width;
347        let rows_count = self.layout.keys.len();
348        let kh_2 =
349            (rect.height() as f32) / (rows_count as f32 + PADDING_RATIO * (rows_count + 1) as f32);
350        let key_height = kh_1.min(kh_2);
351        let padding = PADDING_RATIO * key_height;
352
353        let rows_height = key_height * rows_count as f32 + padding * (rows_count + 1) as f32;
354        let big_height = scale_by_dpi(BIG_BAR_HEIGHT, dpi) as i32;
355        let height_gap = (rect.height() - rows_height.round() as u32) / big_height as u32;
356        rect.min.y += height_gap as i32 * big_height;
357
358        let start_y = rect.min.y as f32 + padding + (rect.height() as f32 - rows_height) / 2.0;
359        let mut index = 0;
360
361        for (i, row) in self.layout.keys.iter().enumerate() {
362            let y = start_y + i as f32 * (padding + key_height);
363            let row_width = (self.layout.widths[i].len() + 1) as f32 * padding
364                + self.layout.widths[i].iter().sum::<f32>() * key_height;
365            let start_x = rect.min.x as f32 + padding + (rect.width() as f32 - row_width) / 2.0;
366            let mut dx = 0.0;
367
368            for j in 0..row.len() {
369                let key_width = self.layout.widths[i][j] * key_height;
370                let x = start_x + dx;
371                dx += key_width + padding;
372                let key_rect = rect![
373                    x.round() as i32,
374                    y.round() as i32,
375                    (x + key_width).round() as i32,
376                    (y + key_height).round() as i32
377                ];
378                self.children[index].resize(key_rect, hub, rq, context);
379                index += 1;
380            }
381        }
382
383        self.rect = rect;
384        context.kb_rect = rect;
385    }
386
387    fn is_background(&self) -> bool {
388        true
389    }
390
391    fn rect(&self) -> &Rectangle {
392        &self.rect
393    }
394
395    fn rect_mut(&mut self) -> &mut Rectangle {
396        &mut self.rect
397    }
398
399    fn children(&self) -> &Vec<Box<dyn View>> {
400        &self.children
401    }
402
403    fn children_mut(&mut self) -> &mut Vec<Box<dyn View>> {
404        &mut self.children
405    }
406
407    fn id(&self) -> Id {
408        self.id
409    }
410}
411
412lazy_static! {
413    // Most of the combination sequences come from X.org.
414    // The chosen characters come from the layout described by
415    // Robert Bringhurst in *The Elements of Typographic Style*,
416    // version 3.1, p. 92.
417    pub static ref DEFAULT_COMBINATIONS: FxHashMap<&'static str, char> = {
418        let mut m = FxHashMap::default();
419        m.insert("oe", 'œ');
420        m.insert("Oe", 'Œ');
421        m.insert("ae", 'æ');
422        m.insert("AE", 'Æ');
423        m.insert("c,", 'ç');
424        m.insert("C,", 'Ç');
425        m.insert("a;", 'ą');
426        m.insert("e;", 'ę');
427        m.insert("A;", 'Ą');
428        m.insert("E;", 'Ę');
429        m.insert("a~", 'ã');
430        m.insert("o~", 'õ');
431        m.insert("n~", 'ñ');
432        m.insert("A~", 'Ã');
433        m.insert("O~", 'Õ');
434        m.insert("N~", 'Ñ');
435        m.insert("a'", 'á');
436        m.insert("e'", 'é');
437        m.insert("i'", 'í');
438        m.insert("o'", 'ó');
439        m.insert("u'", 'ú');
440        m.insert("y'", 'ý');
441        m.insert("z'", 'ź');
442        m.insert("s'", 'ś');
443        m.insert("c'", 'ć');
444        m.insert("n'", 'ń');
445        m.insert("A'", 'Á');
446        m.insert("E'", 'É');
447        m.insert("I'", 'Í');
448        m.insert("O'", 'Ó');
449        m.insert("U'", 'Ú');
450        m.insert("Y'", 'Ý');
451        m.insert("Z'", 'Ź');
452        m.insert("S'", 'Ś');
453        m.insert("C'", 'Ć');
454        m.insert("N'", 'Ń');
455        m.insert("a`", 'à');
456        m.insert("e`", 'è');
457        m.insert("i`", 'ì');
458        m.insert("o`", 'ò');
459        m.insert("u`", 'ù');
460        m.insert("A`", 'À');
461        m.insert("E`", 'È');
462        m.insert("I`", 'Ì');
463        m.insert("O`", 'Ò');
464        m.insert("U`", 'Ù');
465        m.insert("a^", 'â');
466        m.insert("e^", 'ê');
467        m.insert("i^", 'î');
468        m.insert("o^", 'ô');
469        m.insert("u^", 'û');
470        m.insert("w^", 'ŵ');
471        m.insert("y^", 'ŷ');
472        m.insert("A^", 'Â');
473        m.insert("E^", 'Ê');
474        m.insert("I^", 'Î');
475        m.insert("O^", 'Ô');
476        m.insert("U^", 'Û');
477        m.insert("W^", 'Ŵ');
478        m.insert("Y^", 'Ŷ');
479        m.insert("a:", 'ä');
480        m.insert("e:", 'ë');
481        m.insert("i:", 'ï');
482        m.insert("o:", 'ö');
483        m.insert("u:", 'ü');
484        m.insert("y:", 'ÿ');
485        m.insert("A:", 'Ä');
486        m.insert("E:", 'Ë');
487        m.insert("I:", 'Ï');
488        m.insert("O:", 'Ö');
489        m.insert("U:", 'Ü');
490        m.insert("Y:", 'Ÿ');
491        m.insert("u\"", 'ű');
492        m.insert("o\"", 'ő');
493        m.insert("U\"", 'Ű');
494        m.insert("O\"", 'Ő');
495        m.insert("z.", 'ż');
496        m.insert("Z.", 'Ż');
497        m.insert("th", 'þ');
498        m.insert("Th", 'Þ');
499        m.insert("ao", 'å');
500        m.insert("Ao", 'Å');
501        m.insert("l/", 'ł');
502        m.insert("d/", 'đ');
503        m.insert("o/", 'ø');
504        m.insert("L/", 'Ł');
505        m.insert("D/", 'Đ');
506        m.insert("O/", 'Ø');
507        m.insert("mu", 'µ');
508        m.insert("l-", '£');
509        m.insert("pp", '¶');
510        m.insert("so", '§');
511        m.insert("|-", '†');
512        m.insert("|=", '‡');
513        m.insert("ss", 'ß');
514        m.insert("Ss", 'ẞ');
515        m.insert("o_", 'º');
516        m.insert("a_", 'ª');
517        m.insert("oo", '°');
518        m.insert("!!", '¡');
519        m.insert("??", '¿');
520        m.insert(".-", '·');
521        m.insert(".=", '•');
522        m.insert(".>", '›');
523        m.insert(".<", '‹');
524        m.insert("'1", '′');
525        m.insert("'2", '″');
526        m.insert("[[", '⟦');
527        m.insert("]]", '⟧');
528        m.insert("+-", '±');
529        m.insert("-:", '÷');
530        m.insert("<=", '≤');
531        m.insert(">=", '≥');
532        m.insert("=/", '≠');
533        m.insert("-,", '¬');
534        m.insert("~~", '≈');
535        m.insert("<<", '«');
536        m.insert(">>", '»');
537        m.insert("12", '½');
538        m.insert("13", '⅓');
539        m.insert("23", '⅔');
540        m.insert("14", '¼');
541        m.insert("34", '¾');
542        m.insert("15", '⅕');
543        m.insert("25", '⅖');
544        m.insert("35", '⅗');
545        m.insert("45", '⅘');
546        m.insert("16", '⅙');
547        m.insert("56", '⅚');
548        m.insert("18", '⅛');
549        m.insert("38", '⅜');
550        m.insert("58", '⅝');
551        m.insert("78", '⅞');
552        m.insert("#f", '♭');
553        m.insert("#n", '♮');
554        m.insert("#s", '♯');
555        m.insert("%o", '‰');
556        m.insert("e=", '€');
557        m.insert("or", '®');
558        m.insert("oc", '©');
559        m.insert("op", '℗');
560        m.insert("tm", '™');
561        m
562    };
563}