feat(input): implement IME preedit state machine and cursor area tracker

This commit is contained in:
katR 2026-09-30 07:43:09 +05:30
parent be35eaaffd
commit 8a7a05ec9a
4 changed files with 113 additions and 0 deletions

View file

@ -0,0 +1,88 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Rect {
pub x: i32,
pub y: i32,
pub width: i32,
pub height: i32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ImeState {
Inactive,
Active,
}
pub struct ImeManager {
state: ImeState,
cursor_area: Rect,
preedit_text: String,
cursor_pos: usize,
}
impl Default for ImeManager {
fn default() -> Self {
Self::new()
}
}
impl ImeManager {
pub fn new() -> Self {
Self {
state: ImeState::Inactive,
cursor_area: Rect::default(),
preedit_text: String::new(),
cursor_pos: 0,
}
}
pub fn is_active(&self) -> bool {
self.state == ImeState::Active
}
pub fn start_text_input(&mut self, area: Rect) {
self.state = ImeState::Active;
self.cursor_area = area;
self.preedit_text.clear();
self.cursor_pos = 0;
}
pub fn stop_text_input(&mut self) {
self.state = ImeState::Inactive;
self.preedit_text.clear();
self.cursor_pos = 0;
}
pub fn set_cursor_area(&mut self, area: Rect) {
self.cursor_area = area;
}
pub fn cursor_area(&self) -> Rect {
self.cursor_area
}
pub fn update_preedit(&mut self, text: &str, cursor_pos: usize) {
self.preedit_text = text.to_string();
self.cursor_pos = cursor_pos.min(self.preedit_text.chars().count());
}
pub fn preedit_text(&self) -> &str {
&self.preedit_text
}
pub fn cursor_pos(&self) -> usize {
self.cursor_pos
}
pub fn commit_text(&mut self, text: &str) -> String {
self.preedit_text.clear();
self.cursor_pos = 0;
text.to_string()
}
pub fn cancel_composition(&mut self) {
self.preedit_text.clear();
self.cursor_pos = 0;
}
}

View file

@ -0,0 +1,2 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pub mod ime;

View file

@ -2,6 +2,7 @@
pub mod audio;
pub mod dynamic;
pub mod elf;
pub mod input;
pub mod jni;
pub mod linker;
pub mod mmap;

View file

@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-3.0-or-later
use molotov_core::input::ime::{ImeManager, Rect};
#[test]
fn test_ime_preedit_composition_flow() {
let mut ime = ImeManager::new();
ime.start_text_input(Rect { x: 100, y: 200, width: 20, height: 24 });
assert!(ime.is_active());
// User types in Japanese IME: preedit string "とうきょう"
ime.update_preedit("とうきょう", 4);
assert_eq!(ime.preedit_text(), "とうきょう");
assert_eq!(ime.cursor_pos(), 4);
// User selects candidate: commit "東京"
let committed = ime.commit_text("東京");
assert_eq!(committed, "東京");
assert_eq!(ime.preedit_text(), "", "Preedit must clear on commit");
ime.stop_text_input();
assert!(!ime.is_active());
}