use egui::{Event, ImeEvent};
fn last_was_empty_id() -> egui::Id {
egui::Id::new("patina-ime-last-preedit-empty")
}
pub fn drop_repeated_preedits(ctx: &egui::Context, raw_input: &mut egui::RawInput) {
let mut last_was_empty = ctx
.data(|d| d.get_temp::<bool>(last_was_empty_id()))
.unwrap_or(false);
raw_input.events.retain(|event| match event {
Event::Ime(ImeEvent::Preedit { text, .. }) if text.is_empty() => {
!std::mem::replace(&mut last_was_empty, true)
}
Event::Ime(ImeEvent::Preedit { .. } | ImeEvent::Commit(_)) => {
last_was_empty = false;
true
}
_ => true,
});
ctx.data_mut(|d| d.insert_temp(last_was_empty_id(), last_was_empty));
}
#[cfg(test)]
mod tests {
use super::*;
fn preedit(text: &str) -> Event {
Event::Ime(ImeEvent::Preedit {
text: text.into(),
active_range_chars: None,
})
}
fn filter(ctx: &egui::Context, events: Vec<Event>) -> Vec<Event> {
let mut input = egui::RawInput {
events,
..Default::default()
};
drop_repeated_preedits(ctx, &mut input);
input.events
}
#[test]
fn keeps_the_first_empty_preedit_and_drops_repeats() {
let ctx = egui::Context::default();
assert_eq!(filter(&ctx, vec![preedit("")]), vec![preedit("")]);
assert_eq!(filter(&ctx, vec![preedit("")]), vec![]);
assert_eq!(filter(&ctx, vec![preedit(""), preedit("")]), vec![]);
}
#[test]
fn a_composition_keeps_its_closing_empty_preedit() {
let ctx = egui::Context::default();
filter(&ctx, vec![preedit("")]);
assert_eq!(
filter(&ctx, vec![preedit("ce"), preedit("")]),
vec![preedit("ce"), preedit("")]
);
let commit = Event::Ime(ImeEvent::Commit("测".into()));
assert_eq!(
filter(&ctx, vec![commit.clone(), preedit("")]),
vec![commit, preedit("")]
);
}
#[test]
fn other_events_pass_through() {
let ctx = egui::Context::default();
filter(&ctx, vec![preedit("")]);
let text = Event::Text("a".into());
assert_eq!(filter(&ctx, vec![text.clone(), preedit("")]), vec![text]);
}
}