egui-elements 1.0.0

Themes, widgets, and components for egui.
Documentation
use crate::theme::Theme;
use crate::widgets::Button;
use egui::*;

use secure_types::SecureString;

/// Which credentials row a [`VirtualKeyboard`] is typing into.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum InputField {
   Username,
   Password,
   ConfirmPassword,
}

/// A virtual keyboard that can be used to edit an input field.
pub struct VirtualKeyboard {
   open: bool,
   active_target: InputField,
   shift_active: bool,
   caps_lock_active: bool,
}

impl VirtualKeyboard {
   /// Create a keyboard. Pass `true` to show it immediately.
   pub fn new(open: bool) -> Self {
      Self {
         open,
         active_target: InputField::Username,
         shift_active: false,
         caps_lock_active: false,
      }
   }

   /// Show the keyboard on the next [`Self::show`].
   pub fn open(&mut self) {
      self.open = true;
   }

   /// Whether the keyboard is currently shown.
   pub fn is_open(&self) -> bool {
      self.open
   }

   /// Hide the keyboard.
   pub fn close(&mut self) {
      self.open = false;
   }

   /// Direct keystrokes at a different credentials row.
   pub fn set_active_target(&mut self, target: InputField) {
      self.active_target = target;
   }

   /// Paint the keyboard and write pressed keys into `target_str`.
   pub fn show(&mut self, target_str: &mut SecureString, ui: &mut Ui) {
      if !self.open {
         return;
      }

      // Define the keyboard layout
      let keys_layout_lower = vec![
         vec![
            "`",
            "1",
            "2",
            "3",
            "4",
            "5",
            "6",
            "7",
            "8",
            "9",
            "0",
            "-",
            "=",
            "Backspace",
         ],
         vec![
            "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\\",
         ],
         vec![
            "Caps", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "Enter",
         ],
         vec![
            "Shift", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "Shift",
         ],
      ];
      let keys_layout_upper = vec![
         vec![
            "~",
            "!",
            "@",
            "#",
            "$",
            "%",
            "^",
            "&",
            "*",
            "(",
            ")",
            "_",
            "+",
            "Backspace",
         ],
         vec![
            "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{", "}", "|",
         ],
         vec![
            "Caps", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\"", "Enter",
         ],
         vec![
            "Shift", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "Shift",
         ],
      ];

      let theme = Theme::current(ui.ctx());
      let frame = theme.frame2.stroke(Stroke::new(1.0, theme.colors.border));

      frame.show(ui, |ui| {
         ui.vertical_centered(|ui| {
            let is_uppercase = self.shift_active ^ self.caps_lock_active;
            let layout = if is_uppercase {
               &keys_layout_upper
            } else {
               &keys_layout_lower
            };

            for row in layout {
               ui.horizontal(|ui| {
                  for &key in row {
                     let text = RichText::new(key).size(theme.typography.normal);
                     let key_button = Button::new(text).min_size(vec2(30.0, 30.0));
                     if ui.add(key_button).clicked() {
                        self.handle_key_press(key, target_str);
                     }
                  }
               });
            }

            let avail_width = ui.available_width();
            let min_size = vec2((avail_width * 0.5).min(280.0), 30.0);
            let button = Button::new(" ").min_size(min_size);
            if ui.add(button).clicked() {
               target_str.push_str(" ");
            }
         });
      });
   }

   fn handle_key_press(&mut self, key: &str, target: &mut SecureString) {
      match key {
         "Backspace" => {
            target.secure_mut(|s| {
               let len = s.char_len();
               if len > 0 {
                  s.delete_text_char_range(len - 1..len);
               }
            });
         }
         "Shift" => {
            self.shift_active = !self.shift_active;
         }
         "Caps" => {
            self.caps_lock_active = !self.caps_lock_active;
            self.shift_active = false; // Typically, pressing Caps disables Shift
         }
         "Enter" => {
            // For now, we do nothing.
         }
         _ => {
            target.push_str(key);
            // Deactivate shift after a character press
            if self.shift_active {
               self.shift_active = false;
            }
         }
      }
   }
}