Ely Office: six offline-first desktop apps in Rust and GPUI
ElyDoc, ElyTable, ElyShow (LibreOfficeKit), ElyPDF (MuPDF), ElyTree (Git), ElyMark (Milkdown and CodeMirror in CEF). ElyMail and ElyAgent follow.
This commit is contained in:
817 files changed
+143101
No files matched your search
@@ -0,0 +1,189 @@
|
||||
//! GPUI key names as the codes Chromium reads.
|
||||
|
||||
use gpui::Modifiers;
|
||||
|
||||
/// Windows' virtual key, and macOS's hardware key.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct Code {
|
||||
pub windows: i32,
|
||||
pub native: i32,
|
||||
}
|
||||
|
||||
/// Letters, then digits, as macOS numbers its keys.
|
||||
const MAC_LETTERS: [i32; 26] = [
|
||||
0, 11, 8, 2, 14, 3, 5, 4, 34, 38, 40, 37, 46, 45, 31, 35, 12, 15, 1, 17, 32, 9, 13, 7, 16, 6,
|
||||
];
|
||||
const MAC_DIGITS: [i32; 10] = [29, 18, 19, 20, 21, 23, 22, 26, 28, 25];
|
||||
const MAC_F: [i32; 12] = [122, 120, 99, 118, 96, 97, 98, 100, 101, 109, 103, 111];
|
||||
|
||||
/// Named keys: GPUI's name, Windows, macOS.
|
||||
const NAMED: &[(&str, i32, i32)] = &[
|
||||
("backspace", 0x08, 51),
|
||||
("tab", 0x09, 48),
|
||||
("enter", 0x0D, 36),
|
||||
("escape", 0x1B, 53),
|
||||
("space", 0x20, 49),
|
||||
("pageup", 0x21, 116),
|
||||
("pagedown", 0x22, 121),
|
||||
("end", 0x23, 119),
|
||||
("home", 0x24, 115),
|
||||
("left", 0x25, 123),
|
||||
("up", 0x26, 126),
|
||||
("right", 0x27, 124),
|
||||
("down", 0x28, 125),
|
||||
("delete", 0x2E, 117),
|
||||
(";", 0xBA, 41),
|
||||
("=", 0xBB, 24),
|
||||
(",", 0xBC, 43),
|
||||
("-", 0xBD, 27),
|
||||
(".", 0xBE, 47),
|
||||
("/", 0xBF, 44),
|
||||
("`", 0xC0, 50),
|
||||
("[", 0xDB, 33),
|
||||
("\\", 0xDC, 42),
|
||||
("]", 0xDD, 30),
|
||||
("'", 0xDE, 39),
|
||||
];
|
||||
|
||||
pub fn code(key: &str) -> Option<Code> {
|
||||
if let Some(&(_, windows, native)) = NAMED.iter().find(|(name, _, _)| *name == key) {
|
||||
return Some(Code { windows, native });
|
||||
}
|
||||
let mut chars = key.chars();
|
||||
let (Some(c), None) = (chars.next(), chars.next()) else {
|
||||
let n: usize = key.strip_prefix('f')?.parse().ok()?;
|
||||
let native = *MAC_F.get(n.checked_sub(1)?)?;
|
||||
return Some(Code {
|
||||
windows: 0x6F + n as i32,
|
||||
native,
|
||||
});
|
||||
};
|
||||
match c {
|
||||
'a'..='z' => Some(Code {
|
||||
windows: c.to_ascii_uppercase() as i32,
|
||||
native: MAC_LETTERS[(c as u8 - b'a') as usize],
|
||||
}),
|
||||
'0'..='9' => Some(Code {
|
||||
windows: c as i32,
|
||||
native: MAC_DIGITS[(c as u8 - b'0') as usize],
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// The character AppKit gives keys that type none.
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn character(key: &str) -> Option<u16> {
|
||||
let n: Option<u16> = key.strip_prefix('f').and_then(|n| n.parse().ok());
|
||||
Some(match key {
|
||||
"backspace" => 0x7F,
|
||||
"tab" => 0x09,
|
||||
"enter" => 0x0D,
|
||||
"escape" => 0x1B,
|
||||
"up" => 0xF700,
|
||||
"down" => 0xF701,
|
||||
"left" => 0xF702,
|
||||
"right" => 0xF703,
|
||||
"delete" => 0xF728,
|
||||
"home" => 0xF729,
|
||||
"end" => 0xF72B,
|
||||
"pageup" => 0xF72C,
|
||||
"pagedown" => 0xF72D,
|
||||
_ => match n {
|
||||
Some(n @ 1..=12) => 0xF703 + n,
|
||||
_ => return None,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// The control character other systems type, if any.
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
pub fn character(key: &str) -> Option<u16> {
|
||||
Some(match key {
|
||||
"backspace" => 0x08,
|
||||
"tab" => 0x09,
|
||||
"enter" => 0x0D,
|
||||
"escape" => 0x1B,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
/// The chord for cut, copy, paste and select-all here.
|
||||
pub fn edit_chord(modifiers: &Modifiers) -> bool {
|
||||
let primary = if cfg!(target_os = "macos") {
|
||||
modifiers.platform && !modifiers.control
|
||||
} else {
|
||||
modifiers.control && !modifiers.platform
|
||||
};
|
||||
primary && !modifiers.alt && !modifiers.shift
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Code, code};
|
||||
|
||||
#[test]
|
||||
fn keys_carry_both_codes() {
|
||||
assert_eq!(
|
||||
code("a"),
|
||||
Some(Code {
|
||||
windows: 0x41,
|
||||
native: 0
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
code("z"),
|
||||
Some(Code {
|
||||
windows: 0x5A,
|
||||
native: 6
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
code("0"),
|
||||
Some(Code {
|
||||
windows: 0x30,
|
||||
native: 29
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
code("enter"),
|
||||
Some(Code {
|
||||
windows: 0x0D,
|
||||
native: 36
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
code("f1"),
|
||||
Some(Code {
|
||||
windows: 0x70,
|
||||
native: 122
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
code("f12"),
|
||||
Some(Code {
|
||||
windows: 0x7B,
|
||||
native: 111
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
code("["),
|
||||
Some(Code {
|
||||
windows: 0xDB,
|
||||
native: 33
|
||||
})
|
||||
);
|
||||
assert_eq!(code("f13"), None);
|
||||
assert_eq!(code("é"), None);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[test]
|
||||
fn keys_without_text_carry_appkit_characters() {
|
||||
assert_eq!(super::character("backspace"), Some(0x7F));
|
||||
assert_eq!(super::character("up"), Some(0xF700));
|
||||
assert_eq!(super::character("f1"), Some(0xF704));
|
||||
assert_eq!(super::character("f12"), Some(0xF70F));
|
||||
assert_eq!(super::character("a"), None);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user