diff --git a/flat/src/lib.rs b/flat/src/lib.rs index 81c6f21..1494c91 100644 --- a/flat/src/lib.rs +++ b/flat/src/lib.rs @@ -170,7 +170,11 @@ fn hero<'a>(si: usize, f: usize) -> Svgr<'a> { // The newest glyph arrives overexposed and settles. let fresh = n - 1 - i < 2 && f <= hud::TYPE_END && s.kind == Kind::Typing; let fill = if fresh { "#ffffff" } else { "#e9e9e9" }; - fframes::svgr!({c.to_string()}) + if hud::is_wide(c) { + fframes::svgr!({c.to_string()}) + } else { + fframes::svgr!({c.to_string()}) + } }) .collect(); let col = if s.kind == Kind::Cursor { 2 } else { n }; diff --git a/hud/src/lib.rs b/hud/src/lib.rs index c50bb77..696a817 100644 --- a/hud/src/lib.rs +++ b/hud/src/lib.rs @@ -21,6 +21,17 @@ const CHAIN: usize = 5; /// JetBrains Mono advances 0.6 em per character. pub const MONO_ADV: f32 = 0.6; +/// Chinese/Japanese/Korean and full-width characters take a full em. +pub const WIDE_ADV: f32 = 1.0; + +/// True for characters that need the CJK font and a full-width cell. +pub fn is_wide(c: char) -> bool { + matches!(c as u32, 0x1100..=0x115F | 0x2E80..=0xA4CF | 0xAC00..=0xD7A3 | 0xF900..=0xFAFF | 0xFE30..=0xFE4F | 0xFF00..=0xFF60 | 0xFFE0..=0xFFE6) +} + +fn adv_of(c: Option) -> f32 { + match c { Some(c) if is_wide(c) => WIDE_ADV, _ => MONO_ADV } +} pub const LOG_SIZE: f32 = 26.0; pub const LOG_LINE: f32 = 46.0; pub const LOG_X: f32 = 150.0; @@ -292,11 +303,12 @@ pub fn visible(si: usize, f: usize) -> usize { /// Cell of character `i` of the hero line: x, y (top), w, h. pub fn cell(si: usize, i: usize) -> (f32, f32, f32, f32) { let s = scene(si); - let adv = s.size * MONO_ADV; + let chars: Vec = s.text.chars().collect(); + let before: f32 = (0..i).map(|k| adv_of(chars.get(k).copied())).sum(); ( - s.hero.0 + i as f32 * adv, + s.hero.0 + before * s.size, s.hero.1 - s.size * 0.78, - adv, + adv_of(chars.get(i).copied()) * s.size, s.size * 0.98, ) }