Skip to content

Commit

Permalink
feat: Toggle Vietnamese input mode with Fn (globe) key
Browse files Browse the repository at this point in the history
  • Loading branch information
huytd committed Sep 14, 2023
1 parent 131edaa commit 2e05381
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 103 deletions.
123 changes: 70 additions & 53 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use log::debug;
use once_cell::sync::OnceCell;
use platform::{
ensure_accessibility_permission, run_event_listener, send_backspace, send_string, Handle,
KeyModifier, KEY_DELETE, KEY_ENTER, KEY_ESCAPE, KEY_SPACE, KEY_TAB,
KeyModifier, KEY_DELETE, KEY_ENTER, KEY_ESCAPE, KEY_SPACE, KEY_TAB, RAW_KEY_GLOBE, PressedKey
};

use ui::{UIDataAdapter, UPDATE_UI};
Expand Down Expand Up @@ -49,67 +49,84 @@ fn do_restore_word(handle: Handle) {
}
}

fn event_handler(handle: Handle, keycode: Option<char>, modifiers: KeyModifier) -> bool {
unsafe {
match keycode {
Some(keycode) => {
let is_hotkey_pressed = INPUT_STATE.get_hotkey().is_match(modifiers, &keycode);

if is_hotkey_pressed {
INPUT_STATE.toggle_vietnamese();
if let Some(event_sink) = UI_EVENT_SINK.get() {
_ = event_sink.submit_command(UPDATE_UI, (), Target::Auto);
}
return true;
}
unsafe fn toggle_vietnamese() {
println!("UNSAFE TOGGLE VIETNAMESE");
INPUT_STATE.toggle_vietnamese();
if let Some(event_sink) = UI_EVENT_SINK.get() {
_ = event_sink.submit_command(UPDATE_UI, (), Target::Auto);
}
}

if INPUT_STATE.is_enabled() {
match keycode {
KEY_ENTER | KEY_TAB | KEY_SPACE | KEY_ESCAPE => {
let is_valid_word =
vi::validation::is_valid_word(INPUT_STATE.get_displaying_word());
let is_transformed_word = !INPUT_STATE
.get_typing_buffer()
.eq(INPUT_STATE.get_displaying_word());
if is_transformed_word && !is_valid_word {
do_restore_word(handle);
}
INPUT_STATE.new_word();
fn event_handler(handle: Handle, pressed_key: Option<PressedKey>, modifiers: KeyModifier) -> bool {
unsafe {
match pressed_key {
Some(pressed_key) => {
match pressed_key {
PressedKey::Raw(raw_keycode) => {
println!("RAW {:02x}", raw_keycode);
if raw_keycode == RAW_KEY_GLOBE {
println!("GLOBE PRESSED");
toggle_vietnamese();
return true;
}
KEY_DELETE => {
INPUT_STATE.clear();
},
PressedKey::Char(keycode) => {
let is_hotkey_pressed = INPUT_STATE.get_hotkey().is_match(modifiers, &keycode);
println!("IS HOT KEY PRESSED? {is_hotkey_pressed}");
if is_hotkey_pressed {
toggle_vietnamese();
return true;
}
c => {
if "()[]{}<>/\\!@#$%^&*-_=+|~`,.;'\"".contains(c)
|| (c.is_numeric() && modifiers.is_shift())
{
// If special characters detected, dismiss the current tracking word
INPUT_STATE.new_word();
} else {
// Otherwise, process the character
if modifiers.is_super()
|| modifiers.is_control()
|| modifiers.is_alt()
{

if INPUT_STATE.is_enabled() {
match keycode {
KEY_ENTER | KEY_TAB | KEY_SPACE | KEY_ESCAPE => {
let is_valid_word =
vi::validation::is_valid_word(INPUT_STATE.get_displaying_word());
let is_transformed_word = !INPUT_STATE
.get_typing_buffer()
.eq(INPUT_STATE.get_displaying_word());
if is_transformed_word && !is_valid_word {
do_restore_word(handle);
}
INPUT_STATE.new_word();
} else if INPUT_STATE.is_tracking() {
INPUT_STATE.push(
if modifiers.is_shift() || modifiers.is_capslock() {
c.to_ascii_uppercase()
} else {
c
},
);
if INPUT_STATE.should_transform_keys(&c) {
let ret = do_transform_keys(handle, false);
INPUT_STATE.stop_tracking_if_needed();
return ret;
}
KEY_DELETE => {
INPUT_STATE.clear();
}
c => {
if "()[]{}<>/\\!@#$%^&*-_=+|~`,.;'\"".contains(c)
|| (c.is_numeric() && modifiers.is_shift())
{
// If special characters detected, dismiss the current tracking word
INPUT_STATE.new_word();
} else {
// Otherwise, process the character
if modifiers.is_super()
|| modifiers.is_control()
|| modifiers.is_alt()
{
INPUT_STATE.new_word();
} else if INPUT_STATE.is_tracking() {
INPUT_STATE.push(
if modifiers.is_shift() || modifiers.is_capslock() {
c.to_ascii_uppercase()
} else {
c
},
);
if INPUT_STATE.should_transform_keys(&c) {
let ret = do_transform_keys(handle, false);
INPUT_STATE.stop_tracking_if_needed();
return ret;
}
}
}
}
}
}
}
}
};
}
None => {
INPUT_STATE.new_word();
Expand Down
98 changes: 49 additions & 49 deletions src/platform/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use self::macos_ext::{
CGEventCreateKeyboardEvent, CGEventKeyboardSetUnicodeString, CGEventTapPostEvent,
};

use super::{CallbackFn, KeyModifier, KEY_DELETE, KEY_ENTER, KEY_ESCAPE, KEY_SPACE, KEY_TAB};
use super::{CallbackFn, KeyModifier, KEY_DELETE, KEY_ENTER, KEY_ESCAPE, KEY_SPACE, KEY_TAB, PressedKey};

pub const SYMBOL_SHIFT: &str = "⇧";
pub const SYMBOL_CTRL: &str = "⌃";
Expand All @@ -41,56 +41,56 @@ pub fn get_home_dir() -> Option<PathBuf> {
}

// List of keycode: https://eastmanreference.com/complete-list-of-applescript-key-codes
fn get_char(keycode: CGKeyCode) -> Option<char> {
fn get_char(keycode: CGKeyCode) -> Option<PressedKey> {
if let Some(key_map) = unsafe { KEYBOARD_LAYOUT_CHARACTER_MAP.get() } {
return match keycode {
0 => Some(key_map[&'a']),
1 => Some(key_map[&'s']),
2 => Some(key_map[&'d']),
3 => Some(key_map[&'f']),
4 => Some(key_map[&'h']),
5 => Some(key_map[&'g']),
6 => Some(key_map[&'z']),
7 => Some(key_map[&'x']),
8 => Some(key_map[&'c']),
9 => Some(key_map[&'v']),
11 => Some(key_map[&'b']),
12 => Some(key_map[&'q']),
13 => Some(key_map[&'w']),
14 => Some(key_map[&'e']),
15 => Some(key_map[&'r']),
16 => Some(key_map[&'y']),
17 => Some(key_map[&'t']),
31 => Some(key_map[&'o']),
32 => Some(key_map[&'u']),
34 => Some(key_map[&'i']),
35 => Some(key_map[&'p']),
37 => Some(key_map[&'l']),
38 => Some(key_map[&'j']),
40 => Some(key_map[&'k']),
45 => Some(key_map[&'n']),
46 => Some(key_map[&'m']),
18 => Some(key_map[&'1']),
19 => Some(key_map[&'2']),
20 => Some(key_map[&'3']),
21 => Some(key_map[&'4']),
22 => Some(key_map[&'6']),
23 => Some(key_map[&'5']),
25 => Some(key_map[&'9']),
26 => Some(key_map[&'7']),
28 => Some(key_map[&'8']),
29 => Some(key_map[&'0']),
27 => Some(key_map[&'-']),
33 => Some(key_map[&'[']),
30 => Some(key_map[&']']),
41 => Some(key_map[&';']),
43 => Some(key_map[&',']),
36 | 52 => Some(KEY_ENTER), // ENTER
49 => Some(KEY_SPACE), // SPACE
48 => Some(KEY_TAB), // TAB
51 => Some(KEY_DELETE), // DELETE
53 => Some(KEY_ESCAPE), // ESC
_ => None,
0 => Some(PressedKey::Char(key_map[&'a'])),
1 => Some(PressedKey::Char(key_map[&'s'])),
2 => Some(PressedKey::Char(key_map[&'d'])),
3 => Some(PressedKey::Char(key_map[&'f'])),
4 => Some(PressedKey::Char(key_map[&'h'])),
5 => Some(PressedKey::Char(key_map[&'g'])),
6 => Some(PressedKey::Char(key_map[&'z'])),
7 => Some(PressedKey::Char(key_map[&'x'])),
8 => Some(PressedKey::Char(key_map[&'c'])),
9 => Some(PressedKey::Char(key_map[&'v'])),
11 => Some(PressedKey::Char(key_map[&'b'])),
12 => Some(PressedKey::Char(key_map[&'q'])),
13 => Some(PressedKey::Char(key_map[&'w'])),
14 => Some(PressedKey::Char(key_map[&'e'])),
15 => Some(PressedKey::Char(key_map[&'r'])),
16 => Some(PressedKey::Char(key_map[&'y'])),
17 => Some(PressedKey::Char(key_map[&'t'])),
31 => Some(PressedKey::Char(key_map[&'o'])),
32 => Some(PressedKey::Char(key_map[&'u'])),
34 => Some(PressedKey::Char(key_map[&'i'])),
35 => Some(PressedKey::Char(key_map[&'p'])),
37 => Some(PressedKey::Char(key_map[&'l'])),
38 => Some(PressedKey::Char(key_map[&'j'])),
40 => Some(PressedKey::Char(key_map[&'k'])),
45 => Some(PressedKey::Char(key_map[&'n'])),
46 => Some(PressedKey::Char(key_map[&'m'])),
18 => Some(PressedKey::Char(key_map[&'1'])),
19 => Some(PressedKey::Char(key_map[&'2'])),
20 => Some(PressedKey::Char(key_map[&'3'])),
21 => Some(PressedKey::Char(key_map[&'4'])),
22 => Some(PressedKey::Char(key_map[&'6'])),
23 => Some(PressedKey::Char(key_map[&'5'])),
25 => Some(PressedKey::Char(key_map[&'9'])),
26 => Some(PressedKey::Char(key_map[&'7'])),
28 => Some(PressedKey::Char(key_map[&'8'])),
29 => Some(PressedKey::Char(key_map[&'0'])),
27 => Some(PressedKey::Char(key_map[&'-'])),
33 => Some(PressedKey::Char(key_map[&'['])),
30 => Some(PressedKey::Char(key_map[&']'])),
41 => Some(PressedKey::Char(key_map[&';'])),
43 => Some(PressedKey::Char(key_map[&','])),
36 | 52 => Some(PressedKey::Char(KEY_ENTER)), // ENTER
49 => Some(PressedKey::Char(KEY_SPACE)), // SPACE
48 => Some(PressedKey::Char(KEY_TAB)), // TAB
51 => Some(PressedKey::Char(KEY_DELETE)), // DELETE
53 => Some(PressedKey::Char(KEY_ESCAPE)), // ESC
_ => Some(PressedKey::Raw(keycode)),
};
}
None
Expand Down
7 changes: 6 additions & 1 deletion src/platform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub use os::{
pub use os::SystemTray;
pub use os::SystemTrayMenuItemKey;

pub const RAW_KEY_GLOBE: u16 = 0xb3;
pub const KEY_ENTER: char = '\x13';
pub const KEY_SPACE: char = '\u{0020}';
pub const KEY_TAB: char = '\x09';
Expand Down Expand Up @@ -114,4 +115,8 @@ impl KeyModifier {
}
}

pub type CallbackFn = dyn Fn(os::Handle, Option<char>, KeyModifier) -> bool;
pub enum PressedKey {
Char(char),
Raw(u16),
}
pub type CallbackFn = dyn Fn(os::Handle, Option<PressedKey>, KeyModifier) -> bool;

0 comments on commit 2e05381

Please sign in to comment.