1use crate::button::ButtonMessages;
6use crate::constants::constants::{
7 ACTIVE_BUTTON_COLOR, BACKGROUND_GREY, BACKGROUND_WHITE, BUTTON_COLOR, KEY_SPECIAL_COLOR,
8 TEXT_COLOR, TEXT_FIELD_FONT_SIZE,
9};
10use crate::keys::{get_accent, Accent, Key, SpecialKey, KEYBOARD};
11#[cfg(not(feature = "ota_ui"))]
12use crate::proxy_view_assistant::ProxyMessages;
13use crate::text_field::{SceneBuilderTextFieldExt, TextField, TextFieldOptions, TextVisibility};
14use anyhow::Error;
15use carnelian::color::Color;
16use carnelian::drawing::{measure_text_width, FontFace};
17use carnelian::input::{self};
18use carnelian::render::Context as RenderContext;
19use carnelian::scene::facets::{
20 FacetId, SetColorMessage, SetTextMessage, TextFacetOptions, TextHorizontalAlignment,
21 TextVerticalAlignment,
22};
23use carnelian::scene::layout::{
24 Alignment, CrossAxisAlignment, Flex, FlexOptions, MainAxisAlignment, MainAxisSize, Stack,
25 StackOptions,
26};
27use carnelian::scene::scene::{Scene, SceneBuilder};
28use carnelian::{
29 make_message, AppSender, Coord, Message, MessageTarget, Point, Size, ViewAssistant,
30 ViewAssistantContext, ViewKey,
31};
32use euclid::{size2, Size2D};
33#[cfg(feature = "ota_ui")]
34use recovery_util::ota::state_machine::Event as StateMachineEvent;
35use std::collections::VecDeque;
36use std::hash::{Hash, Hasher};
37use zx::{Event, MonotonicDuration, MonotonicInstant};
38
39type FieldName = String;
40type InputText = String;
41
42const TEXT_FIELD_WIDTH: f32 = 960.0;
44
45pub enum KeyMessages {
48 Pressed(&'static Key, MonotonicInstant, String),
49}
50
51#[allow(unused)]
52pub enum KeyboardMessages {
53 NoInput,
54 Result(FieldName, InputText),
55}
56
57struct KeyButton {
58 bg_color: Color,
59 bg_color_active: Color,
60 bg_color_disabled: Color,
61 bg_color_special: Color,
62 fg_color: Color,
63 fg_color_disabled: Color,
64 tracking_pointer: Option<input::pointer::PointerId>,
65 active: bool,
66 focused: bool,
67 special_action: bool,
69 key: &'static Key,
70 label_text: String,
71 background: FacetId,
72 label: FacetId,
73}
74
75impl KeyButton {
76 pub fn new(
77 key: &'static Key,
78 font_face: FontFace,
79 font_size: f32,
80 padding: f32,
81 builder: &mut SceneBuilder,
82 ) -> Result<KeyButton, Error> {
83 let options = StackOptions { alignment: Alignment::center(), ..StackOptions::default() };
84 builder.start_group("key_button", Stack::with_options_ptr(options));
85 let text = match key {
86 Key::Letter(letter_key) => &letter_key.lower,
87 Key::Special(_special_key, text) => text,
88 };
89 let label = builder.text(
90 font_face.clone(),
91 text,
92 font_size,
93 Point::zero(),
94 TextFacetOptions {
95 color: TEXT_COLOR,
96 horizontal_alignment: TextHorizontalAlignment::Left,
97 vertical_alignment: TextVerticalAlignment::Top,
98 ..TextFacetOptions::default()
99 },
100 );
101 let bg_size = match key {
102 Key::Letter(_letter_key) => size2(font_size + padding, font_size + padding),
103 Key::Special(_special_key, text) => {
104 let label_width = measure_text_width(&font_face, font_size, text);
105 size2(label_width + padding * 1.5, font_size + padding)
106 }
107 };
108 let corner: Coord = Coord::from(5.0);
109 let background = builder.rounded_rectangle(bg_size, corner, BUTTON_COLOR);
110 builder.end_group();
111 let button = KeyButton {
112 key,
113 fg_color: TEXT_COLOR,
114 bg_color: BACKGROUND_GREY,
115 bg_color_active: ACTIVE_BUTTON_COLOR,
116 fg_color_disabled: TEXT_COLOR,
117 bg_color_disabled: BACKGROUND_GREY,
118 bg_color_special: KEY_SPECIAL_COLOR,
119 tracking_pointer: None,
120 active: false,
121 focused: false,
122 special_action: false,
123 label_text: text.to_string(),
124 background,
125 label,
126 };
127
128 Ok(button)
129 }
130
131 fn update_button_bg_color(&mut self, scene: &mut Scene) {
132 let (label_color, color) = if self.focused {
133 if self.special_action {
134 (self.fg_color, self.bg_color_special)
135 } else if self.active {
136 (self.fg_color, self.bg_color_active)
137 } else {
138 (self.fg_color, self.bg_color)
139 }
140 } else {
141 (self.fg_color_disabled, self.bg_color_disabled)
142 };
143 scene.send_message(&self.background, Box::new(SetColorMessage { color }));
144 scene.send_message(&self.label, Box::new(SetColorMessage { color: label_color }));
145 }
146
147 fn set_label(&mut self, scene: &mut Scene, shift: bool, alt: bool, _symbol: bool) {
148 if let Key::Letter(key) = self.key {
149 self.label_text = if shift {
150 key.upper
151 } else if alt {
152 key.alt
153 } else {
154 key.lower
155 }
156 .to_string();
157 scene.send_message(
158 &self.label,
159 Box::new(SetTextMessage { text: self.label_text.clone() }),
160 );
161 }
162 }
163
164 pub fn set_accented_char(
165 &mut self,
166 scene: &mut Scene,
167 accent_char: &Accent,
168 state_shift: bool,
169 ) {
170 self.label_text =
171 if state_shift { accent_char.upper } else { accent_char.lower }.to_string();
172 scene.send_message(&self.label, Box::new(SetTextMessage { text: self.label_text.clone() }));
173 }
174
175 pub fn set_special_action(&mut self, scene: &mut Scene, special_action: bool) {
176 if self.special_action != special_action {
177 self.special_action = special_action;
178 self.update_button_bg_color(scene);
179 }
180 }
181
182 fn set_active(&mut self, scene: &mut Scene, active: bool) {
183 if self.active != active {
184 self.active = active;
185 self.update_button_bg_color(scene);
186 }
187 }
188
189 pub fn set_focused(&mut self, scene: &mut Scene, focused: bool) {
190 if focused != self.focused {
191 self.focused = focused;
192 self.active = false;
193 self.update_button_bg_color(scene);
194 if !focused {
195 self.tracking_pointer = None;
196 }
197 }
198 }
199
200 pub fn handle_pointer_event(
201 &mut self,
202 scene: &mut Scene,
203 context: &mut ViewAssistantContext,
204 pointer_event: &input::pointer::Event,
205 ) {
206 if !self.focused {
207 return;
208 }
209 let bounds = scene.get_facet_bounds(&self.background);
210
211 if self.tracking_pointer.is_none() {
212 match pointer_event.phase {
213 input::pointer::Phase::Down(location) => {
214 self.set_active(scene, bounds.contains(location.to_f32()));
215 if self.active {
216 self.tracking_pointer = Some(pointer_event.pointer_id.clone());
217 }
218 }
219 _ => (),
220 }
221 } else {
222 let tracking_pointer = self.tracking_pointer.as_ref().expect("tracking_pointer");
223 if tracking_pointer == &pointer_event.pointer_id {
224 match pointer_event.phase {
225 input::pointer::Phase::Moved(location) => {
226 self.set_active(scene, bounds.contains(location.to_f32()));
227 }
228 input::pointer::Phase::Up => {
229 if self.active {
230 context.queue_message(make_message(KeyMessages::Pressed(
231 self.key,
232 MonotonicInstant::get(),
233 self.label_text.clone(),
234 )));
235 }
236 self.tracking_pointer = None;
237 self.set_active(scene, false);
238 }
239 input::pointer::Phase::Remove => {
240 self.set_active(scene, false);
241 self.tracking_pointer = None;
242 }
243 input::pointer::Phase::Cancel => {
244 self.set_active(scene, false);
245 self.tracking_pointer = None;
246 }
247 _ => (),
248 }
249 }
250 }
251 }
252}
253
254impl PartialEq for KeyButton {
255 fn eq(&self, other: &Self) -> bool {
256 self.label_text == other.label_text
257 }
258}
259
260impl Eq for KeyButton {}
261
262impl Hash for KeyButton {
263 fn hash<H: Hasher>(&self, state: &mut H) {
264 self.label_text.hash(state);
265 }
266}
267
268pub struct SceneDetails {
269 text_field: TextField,
270 buttons: VecDeque<KeyButton>,
271 pub(crate) scene: Scene,
272}
273
274pub struct KeyboardViewAssistant {
275 font_face: FontFace,
276 app_sender: AppSender,
277 view_key: ViewKey,
278 focused: bool,
279 field_name: FieldName,
280 user_text: InputText,
281 privacy: TextVisibility,
282 scene_details: Option<SceneDetails>,
283 state_shift: bool,
284 shift_time: MonotonicInstant,
285 sticky_shift: bool,
286 accent_key: Option<&'static Key>,
287 state_alt: bool,
288 state_symbols: bool,
289}
290
291impl KeyboardViewAssistant {
292 pub fn new(
293 app_sender: AppSender,
294 view_key: ViewKey,
295 font_face: FontFace,
296 ) -> Result<KeyboardViewAssistant, Error> {
297 Ok(KeyboardViewAssistant {
298 font_face,
299 app_sender: app_sender,
300 view_key,
301 focused: false,
302 field_name: String::new(),
303 user_text: String::new(),
304 privacy: TextVisibility::Always,
305 scene_details: None,
306 state_shift: false,
307 shift_time: MonotonicInstant::ZERO,
308 sticky_shift: false,
309 state_alt: false,
310 accent_key: None,
311 state_symbols: false,
312 })
313 }
314
315 pub fn set_field_name(&mut self, field_name: FieldName) {
316 self.field_name = field_name.clone();
317 if let Some(scene_details) = &mut self.scene_details {
318 scene_details.text_field.set_title(field_name);
319 }
320 }
321
322 pub fn set_text_field(&mut self, text: InputText) {
323 self.user_text = text.clone();
324 if let Some(scene_details) = &mut self.scene_details {
325 scene_details.text_field.set_text(text);
326 }
327 }
328
329 pub fn set_privacy(&mut self, privacy: TextVisibility) {
330 self.privacy = privacy;
331 if let Some(scene_details) = &mut self.scene_details {
332 scene_details.text_field.set_privacy(privacy);
333 }
334 }
335
336 fn remove_last_char(&mut self) {
337 let mut chars = self.user_text.chars();
338 chars.next_back();
339 self.user_text = chars.as_str().to_string();
340 }
341
342 fn key_press(&mut self, key: &&'static Key, key_cap: &String, time: &MonotonicInstant) {
343 let mut keyboard_changed = false;
344 match key {
345 Key::Letter(letter_key) => {
346 let mut text = key_cap.as_str();
349 if let Some(alt_key) = self.accent_key {
350 if self.state_alt && letter_key.is_alt_accent {
353 if self.accent_key == Some(key) {
354 self.accent_key = None;
357 } else {
358 self.accent_key = Some(key);
360 }
361 self.remove_last_char();
363 self.state_alt = false;
364 keyboard_changed = true;
365 } else {
366 self.accent_key = None;
367 self.remove_last_char();
369 if let Some(accent) = get_accent(alt_key, key) {
371 if self.state_shift {
373 text = accent.upper;
374 } else {
375 text = accent.lower;
376 }
377 }
378 }
379 } else {
380 if self.state_alt && letter_key.is_alt_accent {
381 self.accent_key = Some(*key);
382 self.state_alt = false;
383 keyboard_changed = true;
384 }
385 }
386 if !self.sticky_shift {
387 self.state_shift = false;
388 keyboard_changed = true;
389 }
390 self.user_text.push_str(text);
391 }
392 Key::Special(key, _text) => match key {
393 SpecialKey::DEL => {
394 let mut chars = self.user_text.chars();
395 chars.next_back();
396 self.user_text = chars.into_iter().collect();
397 if self.accent_key.is_some() {
398 self.accent_key = None;
399 keyboard_changed = true;
400 }
401 }
402 SpecialKey::ENTER => {
403 #[cfg(feature = "ota_ui")]
404 self.app_sender.queue_message(
405 MessageTarget::View(self.view_key),
406 make_message(StateMachineEvent::UserInput(self.user_text.clone())),
407 );
408 #[cfg(not(feature = "ota_ui"))]
410 self.app_sender.queue_message(
411 MessageTarget::View(self.view_key),
412 make_message(ProxyMessages::PopViewAssistant),
413 );
414 #[cfg(not(feature = "ota_ui"))]
416 self.app_sender.queue_message(
417 MessageTarget::View(self.view_key),
418 make_message(KeyboardMessages::Result(
419 self.field_name.clone(),
420 self.user_text.clone(),
421 )),
422 );
423 }
424 SpecialKey::ALT => {
425 self.state_alt = !self.state_alt;
426 if self.state_alt {
427 self.state_shift = false;
428 self.accent_key = None;
429 }
430 keyboard_changed = true;
431 }
432 SpecialKey::SHIFT => {
433 if !self.state_shift {
434 self.shift_time = time.clone();
435 self.state_shift = true;
436 self.state_alt = false;
437 } else {
438 if *time - self.shift_time < MonotonicDuration::from_seconds(1) {
440 self.sticky_shift = true;
441 } else {
442 self.sticky_shift = false;
443 self.state_shift = false;
444 }
445 }
446 keyboard_changed = true;
447 }
448 SpecialKey::SPACE => {
449 self.user_text.push_str(" ");
450 }
451 },
452 };
453 if keyboard_changed {
454 if let Some(scene_details) = &mut self.scene_details {
455 for button in &mut scene_details.buttons {
456 button.set_label(
457 &mut scene_details.scene,
458 self.state_shift,
459 self.state_alt,
460 self.state_symbols,
461 );
462
463 if let Key::Special(SpecialKey::SHIFT, _) = button.key {
464 button.set_special_action(&mut scene_details.scene, self.state_shift)
465 }
466 if let Key::Special(SpecialKey::ALT, _) = button.key {
467 button.set_special_action(&mut scene_details.scene, self.state_alt);
468 }
469
470 if let Key::Letter(letter_key) = button.key {
471 button.set_special_action(&mut scene_details.scene, false);
473 if letter_key.is_alt_accent {
475 button.set_special_action(&mut scene_details.scene, self.state_alt);
476 }
477
478 if let Some(accent_key) = self.accent_key {
480 if let Some(accent_char) = get_accent(accent_key, button.key) {
481 button.set_accented_char(
482 &mut scene_details.scene,
483 accent_char,
484 self.state_shift,
485 );
486 button.set_special_action(&mut scene_details.scene, true);
487 }
488 }
489 }
490 }
491 }
492 }
493
494 if let Some(scene_details) = self.scene_details.as_mut() {
495 scene_details.text_field.update_text(&mut scene_details.scene, self.user_text.clone());
496 }
497 }
498
499 pub fn keyboard_scene(&mut self, context: &ViewAssistantContext) -> SceneDetails {
500 let scene_details = self.scene_details.take().unwrap_or_else(|| {
501 let target_size = context.size;
502 let min_dimension = target_size.width.min(target_size.height);
503 let font_size = (min_dimension / 5.0).ceil().min(40.0);
504 let padding = (min_dimension / 20.0).ceil().max(8.0);
505 let mut builder = SceneBuilder::new().background_color(BACKGROUND_WHITE);
506 let mut text_field: Option<TextField> = None;
507 let mut buttons: VecDeque<KeyButton> = VecDeque::with_capacity(50);
508 builder
509 .group()
510 .column()
511 .max_size()
512 .main_align(MainAxisAlignment::SpaceEvenly)
513 .contents(|builder| {
514 builder.start_group(
515 "text_field_row",
516 Flex::with_options_ptr(FlexOptions::row(
517 MainAxisSize::Max,
518 MainAxisAlignment::Start,
519 CrossAxisAlignment::End,
520 )),
521 );
522 builder.space(Size2D { width: 20.0, height: 10.0, _unit: Default::default() });
524 text_field = Some(builder.text_field(
525 self.field_name.to_string(),
526 self.user_text.to_string(),
527 self.privacy,
528 size2(TEXT_FIELD_WIDTH, TEXT_FIELD_FONT_SIZE * 3.0),
529 TextFieldOptions {
530 text_size: TEXT_FIELD_FONT_SIZE,
531 ..TextFieldOptions::default()
532 },
533 ));
534
535 builder.end_group(); for row in KEYBOARD {
538 builder.start_group(
539 "row",
540 Flex::with_options_ptr(FlexOptions::row(
541 MainAxisSize::Max,
542 MainAxisAlignment::SpaceEvenly,
543 CrossAxisAlignment::Center,
544 )),
545 );
546 for key in *row {
547 let button = KeyButton::new(
548 key,
549 self.font_face.clone(),
550 font_size,
551 padding,
552 builder,
553 )
554 .expect("KeyButton");
555 buttons.push_back(button);
556 }
557 builder.end_group();
558 }
559 });
560 let mut scene = builder.build();
561 scene.layout(target_size);
562 for button in buttons.iter_mut() {
563 button.set_focused(&mut scene, self.focused);
564 }
565 let mut text_field = text_field.expect("text field should have been set properly");
566 text_field.set_focused(&mut scene, self.focused);
567 SceneDetails { scene, text_field, buttons }
568 });
569 scene_details
570 }
571}
572
573impl ViewAssistant for KeyboardViewAssistant {
574 fn resize(&mut self, _new_size: &Size) -> Result<(), Error> {
575 self.scene_details = None;
576 Ok(())
577 }
578
579 fn render(
580 &mut self,
581 render_context: &mut RenderContext,
582 ready_event: Event,
583 context: &ViewAssistantContext,
584 ) -> Result<(), Error> {
585 let mut scene_details = self.keyboard_scene(context);
586
587 scene_details.scene.render(render_context, ready_event, context)?;
588 self.scene_details = Some(scene_details);
589 context.request_render();
590 Ok(())
591 }
592
593 fn handle_message(&mut self, message: Message) {
594 if let Some(key_message) = message.downcast_ref::<KeyMessages>() {
595 match key_message {
596 KeyMessages::Pressed(key, time, letter) => {
597 self.key_press(key, letter, time);
598 }
599 }
600 }
601 if let Some(button_message) = message.downcast_ref::<ButtonMessages>() {
602 match button_message {
603 ButtonMessages::Pressed(_time, button_text) if button_text == &self.field_name => {
604 self.privacy = self.privacy.toggle();
605 self.scene_details = None;
606 }
607 &_ => {}
608 }
609 }
610 }
611
612 fn handle_pointer_event(
613 &mut self,
614 context: &mut ViewAssistantContext,
615 _event: &input::Event,
616 pointer_event: &input::pointer::Event,
617 ) -> Result<(), Error> {
618 if let Some(scene_details) = self.scene_details.as_mut() {
619 for button in scene_details.buttons.iter_mut() {
620 button.handle_pointer_event(&mut scene_details.scene, context, &pointer_event);
621 }
622 scene_details.text_field.handle_pointer_event(
623 &mut scene_details.scene,
624 context,
625 &pointer_event,
626 );
627 context.request_render();
628 }
629 Ok(())
630 }
631
632 fn handle_focus_event(
633 &mut self,
634 context: &mut ViewAssistantContext,
635 focused: bool,
636 ) -> Result<(), Error> {
637 self.focused = focused;
638 if let Some(scene_details) = self.scene_details.as_mut() {
639 for button in scene_details.buttons.iter_mut() {
640 button.set_focused(&mut scene_details.scene, focused);
641 }
642 scene_details.text_field.set_focused(&mut scene_details.scene, focused);
643 }
644 context.request_render();
645 Ok(())
646 }
647}