use super::{KeyBehavior, SyllableEditor};
use crate::{
input::KeyboardEvent,
syl,
zhuyin::{Bopomofo, BopomofoKind, Syllable},
};
#[derive(Debug, Clone, Copy)]
pub struct Hsu {
syllable: Syllable,
}
impl Hsu {
pub fn new() -> Hsu {
Hsu {
syllable: Default::default(),
}
}
fn is_hsu_end_key(&self, key: KeyboardEvent) -> bool {
match key.ksym.to_unicode() {
's' | 'd' | 'f' | 'j' | ' ' => !self.syllable.is_empty(),
_ => false,
}
}
fn has_initial_or_medial(&self) -> bool {
self.syllable.has_initial() || self.syllable.has_medial()
}
const ALT_TABLE: &'static [(Syllable, &'static [Syllable])] = &[
(syl![Bopomofo::C], &[syl![Bopomofo::EI]]),
(syl![Bopomofo::I], &[syl![Bopomofo::EH]]),
(syl![Bopomofo::S], &[syl![Bopomofo::TONE5]]),
(syl![Bopomofo::D], &[syl![Bopomofo::TONE2]]),
(syl![Bopomofo::F], &[syl![Bopomofo::TONE3]]),
(syl![Bopomofo::E], &[syl![Bopomofo::G]]),
(syl![Bopomofo::O], &[syl![Bopomofo::H]]),
(
syl![Bopomofo::ZH],
&[syl![Bopomofo::J], syl![Bopomofo::TONE4]],
),
(syl![Bopomofo::ANG], &[syl![Bopomofo::K]]),
(
syl![Bopomofo::ER],
&[syl![Bopomofo::L], syl![Bopomofo::ENG]],
),
(syl![Bopomofo::SH], &[syl![Bopomofo::X]]),
(syl![Bopomofo::CH], &[syl![Bopomofo::Q]]),
(syl![Bopomofo::EN], &[syl![Bopomofo::N]]),
(syl![Bopomofo::AN], &[syl![Bopomofo::M]]),
];
}
impl Default for Hsu {
fn default() -> Self {
Self::new()
}
}
impl SyllableEditor for Hsu {
fn key_press(&mut self, key: KeyboardEvent) -> KeyBehavior {
if self.is_hsu_end_key(key) {
if !self.syllable.has_medial() && !self.syllable.has_rime() {
if let Some(key) = self.syllable.initial() {
match key {
Bopomofo::J => {
self.syllable.update(Bopomofo::ZH);
}
Bopomofo::Q => {
self.syllable.update(Bopomofo::CH);
}
Bopomofo::X => {
self.syllable.update(Bopomofo::SH);
}
Bopomofo::H => {
self.syllable.remove_initial();
self.syllable.update(Bopomofo::O);
}
Bopomofo::G => {
self.syllable.remove_initial();
self.syllable.update(Bopomofo::E);
}
Bopomofo::M => {
self.syllable.remove_initial();
self.syllable.update(Bopomofo::AN);
}
Bopomofo::N => {
self.syllable.remove_initial();
self.syllable.update(Bopomofo::EN);
}
Bopomofo::K => {
self.syllable.remove_initial();
self.syllable.update(Bopomofo::ANG);
}
Bopomofo::L => {
self.syllable.remove_initial();
self.syllable.update(Bopomofo::ER);
}
_ => (),
}
}
}
match (self.syllable.initial(), self.syllable.medial()) {
(Some(Bopomofo::G), Some(Bopomofo::I))
| (Some(Bopomofo::G), Some(Bopomofo::IU)) => {
self.syllable.update(Bopomofo::J);
}
_ => (),
}
match key.ksym.to_unicode() {
'd' => self.syllable.update(Bopomofo::TONE2),
'f' => self.syllable.update(Bopomofo::TONE3),
'j' => self.syllable.update(Bopomofo::TONE4),
's' => self.syllable.update(Bopomofo::TONE5),
_ => {
self.syllable.remove_tone();
}
};
KeyBehavior::Commit
} else {
let bopomofo = match key.ksym.to_unicode() {
'a' => {
if self.has_initial_or_medial() {
Bopomofo::EI
} else {
Bopomofo::C
}
}
'b' => Bopomofo::B,
'c' => Bopomofo::SH,
'd' => Bopomofo::D,
'e' => {
if self.syllable.has_medial() {
Bopomofo::EH
} else {
Bopomofo::I
}
}
'f' => Bopomofo::F,
'g' => {
if self.has_initial_or_medial() {
Bopomofo::E
} else {
Bopomofo::G
}
}
'h' => {
if self.has_initial_or_medial() {
Bopomofo::O
} else {
Bopomofo::H
}
}
'i' => Bopomofo::AI,
'j' => Bopomofo::ZH,
'k' => {
if self.has_initial_or_medial() {
Bopomofo::ANG
} else {
Bopomofo::K
}
}
'l' => {
if self.has_initial_or_medial() {
Bopomofo::ENG
} else {
Bopomofo::L
}
}
'm' => {
if self.has_initial_or_medial() {
Bopomofo::AN
} else {
Bopomofo::M
}
}
'n' => {
if self.has_initial_or_medial() {
Bopomofo::EN
} else {
Bopomofo::N
}
}
'o' => Bopomofo::OU,
'p' => Bopomofo::P,
'r' => Bopomofo::R,
's' => Bopomofo::S,
't' => Bopomofo::T,
'u' => Bopomofo::IU,
'v' => Bopomofo::CH,
'w' => Bopomofo::AU,
'x' => Bopomofo::U,
'y' => Bopomofo::A,
'z' => Bopomofo::Z,
_ => return KeyBehavior::NoWord,
};
let kind = bopomofo.kind();
match (self.syllable.initial(), self.syllable.medial()) {
(Some(Bopomofo::G), Some(Bopomofo::I))
| (Some(Bopomofo::G), Some(Bopomofo::IU)) => {
self.syllable.update(Bopomofo::J);
}
_ => (),
}
if (kind == BopomofoKind::Medial && bopomofo == Bopomofo::U)
|| (kind == BopomofoKind::Rime && self.syllable.medial().is_none())
{
match self.syllable.initial() {
Some(Bopomofo::J) => {
self.syllable.update(Bopomofo::ZH);
}
Some(Bopomofo::Q) => {
self.syllable.update(Bopomofo::CH);
}
Some(Bopomofo::X) => {
self.syllable.update(Bopomofo::SH);
}
_ => (),
}
}
if bopomofo == Bopomofo::I || bopomofo == Bopomofo::IU {
match self.syllable.initial() {
Some(Bopomofo::ZH) => {
self.syllable.update(Bopomofo::J);
}
Some(Bopomofo::CH) => {
self.syllable.update(Bopomofo::Q);
}
Some(Bopomofo::SH) => {
self.syllable.update(Bopomofo::X);
}
_ => (),
}
}
self.syllable.update(bopomofo);
KeyBehavior::Absorb
}
}
fn is_empty(&self) -> bool {
self.syllable.is_empty()
}
fn remove_last(&mut self) {
self.syllable.pop();
}
fn clear(&mut self) {
self.syllable.clear();
}
fn read(&self) -> Syllable {
self.syllable
}
fn alt_syllables(&self, syl: Syllable) -> &[Syllable] {
for entry in Self::ALT_TABLE {
if entry.0 == syl {
return entry.1;
}
}
&[]
}
fn clone(&self) -> Box<dyn SyllableEditor> {
Box::new(Clone::clone(self))
}
}
#[cfg(test)]
mod test {
use super::Hsu;
use crate::{
editor::zhuyin_layout::SyllableEditor,
input::{
KeyboardEvent,
keysym::{self, Keysym},
},
zhuyin::Bopomofo,
};
fn map_key(ksym: Keysym) -> KeyboardEvent {
KeyboardEvent::builder().ksym(ksym).build()
}
#[test]
fn cen() {
let mut hsu = Hsu::new();
hsu.key_press(map_key(keysym::SYM_LOWER_C));
hsu.key_press(map_key(keysym::SYM_LOWER_E));
hsu.key_press(map_key(keysym::SYM_LOWER_N));
hsu.key_press(map_key(keysym::SYM_SPACE));
let result = hsu.read();
assert_eq!(result.initial(), Some(Bopomofo::X));
assert_eq!(result.medial(), Some(Bopomofo::I));
assert_eq!(result.rime(), Some(Bopomofo::EN));
}
#[test]
fn convert_n_to_en() {
let mut hsu = Hsu::new();
hsu.key_press(map_key(keysym::SYM_LOWER_N));
hsu.key_press(map_key(keysym::SYM_LOWER_F));
let result = hsu.read();
assert_eq!(result.rime(), Some(Bopomofo::EN));
}
}