use std::vec; use std::vec::Vec; use std::collections::HashMap; use ming_wm_lib::window_manager_types::{ DrawInstructions, WindowLike, WindowLikeType, KeyChar }; use ming_wm_lib::messages::{ WindowMessage, WindowMessageResponse, WindowManagerRequest }; use ming_wm_lib::framebuffer_types::Dimensions; use ming_wm_lib::themes::ThemeInfo; use ming_wm_lib::utils::point_inside; use ming_wm_lib::components::Component; use ming_wm_lib::components::press_button::PressButton; //seems like framebuffer only updates if (real) key is pressed... //on mobile, volume down button seems to work but is annoying const PADDING_Y: usize = 15; const PADDING_X: usize = 15; //padding in between keys in the x direction const KEY_PADDING_X: usize = 5; const KEY_PADDING_Y: usize = 5; #[derive(Clone, Default, Eq, PartialEq, Hash)] enum Board { #[default] Regular, Shift, Symbols, SymbolsShift, } impl Board { fn inc(&mut self) -> Self { match self { Board::Regular => Board::Shift, Board::Shift => Board::Symbols, Board::Symbols => Board::SymbolsShift, Board::SymbolsShift => Board::Regular, } } } #[derive(Clone)] enum KeyResponse { Key(char), Alt, Ctrl, SwitchBoard, } //if alt is true and ctrl is true, only alt will be sent. //because I don't care about ctrl+alt stuff, and won't use it. //(and probably not supported by this with a real keyboard anyways) #[derive(Default)] pub struct OnscreenKeyboard { dimensions: Dimensions, components: Vec>>, alt: bool, ctrl: bool, board: Board, } impl WindowLike for OnscreenKeyboard { fn handle_message(&mut self, message: WindowMessage) -> WindowMessageResponse { match message { WindowMessage::Init(dimensions) => { self.dimensions = dimensions; self.set_key_components(); WindowMessageResponse::JustRedraw }, WindowMessage::Touch(x, y) => { for c in &mut self.components { if point_inside([x, y], c.top_left, c.size) { let returned = c.handle_message(WindowMessage::Touch(x, y)); if let Some(returned) = returned { return match returned { KeyResponse::Key(ch) => { let kc = if self.alt { self.alt = false; KeyChar::Alt(ch) } else if self.ctrl { self.ctrl = false; KeyChar::Ctrl(ch) } else { KeyChar::Press(ch) }; WindowMessageResponse::Request(WindowManagerRequest::DoKeyChar(kc)) }, KeyResponse::Alt => { self.alt = !self.alt; WindowMessageResponse::DoNothing }, KeyResponse::Ctrl => { self.ctrl = !self.ctrl; WindowMessageResponse::DoNothing }, KeyResponse::SwitchBoard => { self.board = self.board.inc(); self.set_key_components(); WindowMessageResponse::JustRedraw }, }; } } } WindowMessageResponse::DoNothing }, _ => WindowMessageResponse::DoNothing, } } fn draw(&self, theme_info: &ThemeInfo) -> Vec { let mut instructions = vec![DrawInstructions::Rect([0, 0], self.dimensions, theme_info.background)]; for component in &self.components { instructions.extend(component.draw(theme_info)); } instructions } fn subtype(&self) -> WindowLikeType { WindowLikeType::OnscreenKeyboard } fn ideal_dimensions(&self, dimensions: Dimensions) -> Dimensions { [dimensions[0] - 180, 250] } } impl OnscreenKeyboard { pub fn new() -> Self { Default::default() } fn set_key_components(&mut self) { self.components = Vec::new(); let rows: [HashMap>; 4] = [ HashMap::from([ (Board::Regular, vec!['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p']), (Board::Shift, vec!['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P']), (Board::Symbols, vec!['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']), (Board::SymbolsShift, vec![]), //empty ]), HashMap::from([ (Board::Regular, vec!['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l']), (Board::Shift, vec!['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L']), (Board::Symbols, vec!['|', '=', '$', '%', '&', '-', '+', '(', ')']), (Board::SymbolsShift, vec!['`', '@', '#', '^']), ]), HashMap::from([ (Board::Regular, vec!['𐘃', 'z', 'x', 'c', 'v', 'b', 'n', 'm', '𐘁']), //escape and backspace (Board::Shift, vec!['𐘃', 'z', 'x', 'c', 'v', 'b', 'n', 'm', '𐘁']), //escape and backspace (Board::Symbols, vec!['~', '*', '"', '\'', ':', ';', '!', '?', '𐘁']), //backspace (Board::SymbolsShift, vec!['[', ']', '{', '}', '𐘁']), //backspace ]), HashMap::from([ (Board::Regular, vec!['𐘧', '𐘎', ' ', '𐘂']), //switch board (special case, not a real key), alt (special case, not a real key), enter (Board::Shift, vec!['𐘧', '𐘎', ' ', '𐘂']), //switch board (special case, not a real key), alt (special case, not a real key), enter (Board::Symbols, vec!['𐘧', '𐘎', ',', '_', ' ', '/', '.', '𐘂']), //switch board (special case, not a real key), alt (special case, not a real key), enter (Board::SymbolsShift, vec!['𐘧', '𐘎', '\\', '<', ' ', '>', '𐘾', '𐘂']), //switch board (special case, not a real key), alt (special case, not a real key), ctrl (special case, not a real key), enter //ctrl = shimazu ]), ]; //hardcoded for now let mut y = PADDING_Y; let key_height = (self.dimensions[1] - PADDING_Y * 2 - KEY_PADDING_Y * (rows.len() - 1)) / rows.len(); let reg_key_width = (self.dimensions[0] - PADDING_X * 2 - KEY_PADDING_X * (10 - 1)) / 10; for row in rows { let row_keys = &row[&self.board]; //centre let mut x = PADDING_X + (10 - row_keys.len()) * (reg_key_width + KEY_PADDING_X) / 2; for key in row_keys { let press_return = if key == &'𐘧' { KeyResponse::SwitchBoard } else if key == &'𐘎' { KeyResponse::Alt } else if key == &'𐘾' { KeyResponse::Ctrl } else { KeyResponse::Key(*key) }; let mut text = key.to_string(); if text == "𐘧" { text = "Switch".to_string(); } else if text == "𐘎" { text = "Alt".to_string(); } else if text == "𐘂" { text = "Enter".to_string(); } else if text == "𐘁" { text = "Back".to_string(); } else if text == "𐘃" { text = "Esc".to_string(); } else if text == "𐘾" { text = "Ctrl".to_string(); } self.components.push(Box::new(PressButton::new([x, y], [reg_key_width, key_height], text, press_return))); x += reg_key_width + KEY_PADDING_X; } y += key_height + KEY_PADDING_Y; } } }