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 *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(®ion, 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 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}