diff --git a/service/src/convert.rs b/service/src/convert.rs index 6ac7dbf..9b0d066 100644 --- a/service/src/convert.rs +++ b/service/src/convert.rs @@ -15,8 +15,8 @@ pub fn from_event(event: &rdev::Event) -> KeyboardEvent { .filter(|c| c.is_alphanumeric() || c.is_ascii_punctuation()) .map(|c| Key::Character(c.to_string())); let (state, key) = match event.event_type { - rdev::EventType::KeyPress(key) => (KeyState::Down, from_key(key)), - rdev::EventType::KeyRelease(key) => (KeyState::Up, from_key(key)), + rdev::EventType::KeyPress(key) => (KeyState::Down, from_key(&key)), + rdev::EventType::KeyRelease(key) => (KeyState::Up, from_key(&key)), _ => Default::default(), }; @@ -28,7 +28,7 @@ pub fn from_event(event: &rdev::Event) -> KeyboardEvent { } /// Converts an rdev::Key into a Key. -pub fn from_key(key: rdev::Key) -> Key { +pub fn from_key(key: &rdev::Key) -> Key { match key { rdev::Key::Alt => Key::Named(Alt), rdev::Key::AltGr => Key::Named(AltGraph), diff --git a/service/src/frontend/console.rs b/service/src/frontend/console.rs index f0773b0..7233db1 100644 --- a/service/src/frontend/console.rs +++ b/service/src/frontend/console.rs @@ -13,6 +13,7 @@ pub struct Console { predicates: Vec, current_predicate_id: usize, input: String, + output: String, idle_state: bool, tx: Option>, rx: Option>, @@ -37,6 +38,7 @@ impl Frontend for Console { let command = self.rx.as_ref().unwrap().recv()?; match command { Command::InputText(input) => self.set_input_text(input.to_owned()), + Command::OutputText(output) => self.set_output_text(output.to_owned()), Command::PageSize(size) => self.set_max_predicates(size), Command::State(state) => self.set_state(state), Command::Predicate(predicate) => self.add_predicate(predicate.to_owned()), @@ -61,6 +63,7 @@ impl Frontend for Console { "_state_" if self.idle_state => { tx.send(Command::State(false))?; self.input = String::default(); + self.output = String::default(); } "_exit_" => { tx.send(Command::End)?; @@ -88,6 +91,9 @@ impl Console { // Input println!("input: {}", self.input); + // Output + println!("output: {}", self.output); + // Predicates let page_size = std::cmp::min(self.page_size, self.predicates.len()); println!( @@ -120,6 +126,7 @@ impl Console { self.predicates.clear(); self.current_predicate_id = 0; self.input = String::default(); + self.output = String::default(); } fn set_max_predicates(&mut self, size: usize) { @@ -131,6 +138,10 @@ impl Console { self.input = text; } + fn set_output_text(&mut self, text: String) { + self.output = text; + } + fn add_predicate(&mut self, predicate: Predicate) { predicate .texts @@ -197,7 +208,16 @@ mod tests { assert_eq!(rx2.recv().unwrap(), Command::NOP); tx1.send(Command::PageSize(10)).unwrap(); + + tx1.send(Command::InputText("ngaf3".to_owned())).unwrap(); + tx1.send(Command::OutputText("ngɑ̄".to_owned())).unwrap(); + tx1.send(Command::Update).unwrap(); + + tx1.send(Command::Clear).unwrap(); + tx1.send(Command::Update).unwrap(); + tx1.send(Command::InputText("he".to_owned())).unwrap(); + tx1.send(Command::OutputText("he".to_owned())).unwrap(); tx1.send(Command::Predicate(Predicate { code: "hell".to_owned(), remaining_code: "llo".to_owned(), @@ -226,8 +246,8 @@ mod tests { can_commit: false, })) .unwrap(); - tx1.send(Command::Update).unwrap(); + tx1.send(Command::SelectPreviousPredicate).unwrap(); tx1.send(Command::SelectedPredicate).unwrap(); assert_eq!( diff --git a/service/src/frontend/message.rs b/service/src/frontend/message.rs index 1b931bc..69e6307 100644 --- a/service/src/frontend/message.rs +++ b/service/src/frontend/message.rs @@ -11,6 +11,8 @@ pub enum Command { Position((f64, f64)), /// Informs about the current input text. InputText(String), + /// Informs about the current output text. + OutputText(String), /// Informs about the max numbers of predicate by page. PageSize(usize), /// Whether the backend is in IDLE. diff --git a/service/src/lib.rs b/service/src/lib.rs index d3c2206..c3b1662 100644 --- a/service/src/lib.rs +++ b/service/src/lib.rs @@ -5,7 +5,7 @@ pub use afrim_config::Config; use afrim_preprocessor::{utils, Command as EventCmd, Preprocessor}; use afrim_translator::Translator; use anyhow::{Context, Result}; -use enigo::{Direction, Enigo, Key, Keyboard}; +use enigo::{Enigo, Keyboard}; use frontend::{Command as GUICmd, Frontend}; use rdev::{self, EventType, Key as E_Key}; use std::{rc::Rc, sync::mpsc, thread}; @@ -17,8 +17,8 @@ pub fn run( ) -> Result<()> { // State. let mut is_ctrl_released = true; - let mut is_backspace_pressed = false; let mut idle = false; + let mut output = String::default(); // Configuration of the afrim. let memory = utils::build_map( @@ -84,6 +84,8 @@ pub fn run( // We process event. for event in event_rx.iter() { + let prev_input = preprocessor.get_input(); + match event.event_type { // Handling of idle state. EventType::KeyPress(E_Key::Pause) => { @@ -104,68 +106,101 @@ pub fn run( EventType::KeyRelease(E_Key::ControlLeft | E_Key::ControlRight) => { is_ctrl_released = true; } - EventType::KeyRelease(E_Key::Backspace) => { - is_backspace_pressed = false; - } _ if idle => (), // Handling of special functions. + // Select next predicate. EventType::KeyRelease(E_Key::ShiftRight) if !is_ctrl_released => { frontend_tx1.send(GUICmd::SelectNextPredicate)?; } + // Select previous predicate. EventType::KeyRelease(E_Key::ShiftLeft) if !is_ctrl_released => { frontend_tx1.send(GUICmd::SelectPreviousPredicate)?; } + // Commit the selected predicate. EventType::KeyRelease(E_Key::Space) if !is_ctrl_released => { rdev::simulate(&EventType::KeyRelease(E_Key::ControlLeft)) .expect("We couldn't cancel the special function key"); frontend_tx1.send(GUICmd::SelectedPredicate)?; if let GUICmd::Predicate(predicate) = frontend_rx2.recv()? { - preprocessor.commit( - predicate - .texts - .first() - .unwrap_or(&String::default()) - .to_owned(), - ); + rdev::simulate(&EventType::KeyPress(E_Key::Pause)).unwrap(); + keyboard + .text(predicate.texts.first().unwrap_or(&String::default())) + .unwrap(); + rdev::simulate(&EventType::KeyRelease(E_Key::Pause)).unwrap(); + + preprocessor.process(Default::default()); + output.clear(); frontend_tx1.send(GUICmd::Clear)?; } } _ if !is_ctrl_released => (), + // Commit the output. + EventType::KeyPress(E_Key::Space) if !prev_input.is_empty() => { + rdev::simulate(&EventType::KeyRelease(E_Key::Space)).unwrap(); + rdev::simulate(&EventType::KeyPress(E_Key::Pause)).unwrap(); + + // Delete the space. + rdev::simulate(&EventType::KeyPress(E_Key::Backspace)).unwrap(); + rdev::simulate(&EventType::KeyRelease(E_Key::Backspace)).unwrap(); + + // Commit + keyboard.text(&output).unwrap(); + + rdev::simulate(&EventType::KeyRelease(E_Key::Pause)).unwrap(); + + preprocessor.process(Default::default()); + output.clear(); + frontend_tx1.send(GUICmd::Clear)?; + } // GUI events. EventType::MouseMove { x, y } => { frontend_tx1.send(GUICmd::Position((x, y)))?; } // Process events. _ => { - if event.event_type == EventType::KeyPress(E_Key::Backspace) { - is_backspace_pressed = true; - } let (changed, _committed) = preprocessor.process(convert::from_event(&event)); + let curr_input = preprocessor.get_input(); - if changed { - let input = preprocessor.get_input(); + if prev_input.len() < curr_input.len() { + output.push(curr_input.chars().last().unwrap_or_default()); + + // Cancel the keyboard input. + rdev::simulate(&EventType::KeyPress(E_Key::Pause)).unwrap(); + rdev::simulate(&EventType::KeyPress(E_Key::Backspace)).unwrap(); + rdev::simulate(&EventType::KeyRelease(E_Key::Backspace)).unwrap(); + rdev::simulate(&EventType::KeyRelease(E_Key::Pause)).unwrap(); + } else if curr_input.is_empty() { + output.clear(); + } + if changed { frontend_tx1.send(GUICmd::Clear)?; translator - .translate(&input) + .translate(&curr_input) .into_iter() .take(page_size * 2) .try_for_each(|predicate| -> Result<()> { if predicate.texts.is_empty() { } else if auto_commit && predicate.can_commit { preprocessor.commit(predicate.texts[0].to_owned()); + + rdev::simulate(&EventType::KeyPress(E_Key::Pause)).unwrap(); + keyboard.text(&predicate.texts[0]).unwrap(); + rdev::simulate(&EventType::KeyRelease(E_Key::Pause)).unwrap(); } else { frontend_tx1.send(GUICmd::Predicate(predicate))?; } Ok(()) })?; - - frontend_tx1.send(GUICmd::InputText(input))?; - frontend_tx1.send(GUICmd::Update)?; } + + // Update frontend. + frontend_tx1.send(GUICmd::InputText(curr_input))?; + frontend_tx1.send(GUICmd::OutputText(output.clone()))?; + frontend_tx1.send(GUICmd::Update)?; } } @@ -173,18 +208,15 @@ pub fn run( while let Some(command) = preprocessor.pop_queue() { match command { EventCmd::CommitText(text) => { - keyboard.text(&text).unwrap(); + output.push_str(&text); + frontend_tx1.send(GUICmd::OutputText(output.clone()))?; + frontend_tx1.send(GUICmd::Update)?; } EventCmd::Delete(text) => { - let mut step = text.chars().count(); - // Prevent an additional backspace. - if is_backspace_pressed { - keyboard.key(Key::Backspace, Direction::Release).unwrap(); - is_backspace_pressed = false; - step -= 1; - } - - (0..step).for_each(|_| keyboard.key(Key::Backspace, Direction::Click).unwrap()); + let step = text.chars().count(); + (0..(if step > 0 { step } else { 1 })).for_each(|_| { + output.pop(); + }); } EventCmd::Pause => { rdev::simulate(&EventType::KeyPress(E_Key::Pause)).unwrap();