use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
use crate::types::ElementId;
use super::state::ImeState;
#[derive(Default)]
pub struct ImeRegistry {
states: HashMap<ElementId, Rc<RefCell<ImeState>>>,
dirty: bool,
}
impl ImeRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn clear(&mut self) {
self.dirty = false;
}
pub fn register(&mut self, id: ElementId) -> Rc<RefCell<ImeState>> {
match self.states.entry(id) {
std::collections::hash_map::Entry::Occupied(e) => e.get().clone(),
std::collections::hash_map::Entry::Vacant(v) => {
self.dirty = true;
v.insert(Rc::new(RefCell::new(ImeState::default()))).clone()
}
}
}
pub fn get(&self, id: ElementId) -> Option<Rc<RefCell<ImeState>>> {
self.states.get(&id).cloned()
}
pub fn prune_missing(&mut self, registered_ids: &HashSet<ElementId>) {
self.states.retain(|id, _| registered_ids.contains(id));
}
pub fn len(&self) -> usize {
self.states.len()
}
pub fn is_empty(&self) -> bool {
self.states.is_empty()
}
pub fn clear_drag_flags(&self) {
for state_rc in self.states.values() {
if let Ok(mut state) = state_rc.try_borrow_mut() {
state.dragging = false;
state.click_count = 0;
}
}
}
}
impl std::fmt::Debug for ImeRegistry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ImeRegistry")
.field("len", &self.states.len())
.field("dirty", &self.dirty)
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn id(n: u64) -> ElementId {
ElementId::from_raw(n)
}
#[test]
fn empty_registry_is_empty() {
let r = ImeRegistry::new();
assert!(r.is_empty());
assert_eq!(r.len(), 0);
assert!(r.get(id(1)).is_none());
}
#[test]
fn register_inserts_default_state() {
let mut r = ImeRegistry::new();
let s = r.register(id(1));
assert_eq!(r.len(), 1);
let borrowed = s.borrow();
assert!(borrowed.text.is_empty());
assert_eq!(borrowed.caret, 0);
assert!(borrowed.preedit.is_none());
}
#[test]
fn register_idempotent_returns_same_rc() {
let mut r = ImeRegistry::new();
let s1 = r.register(id(1));
let s2 = r.register(id(1));
assert_eq!(r.len(), 1);
assert!(Rc::ptr_eq(&s1, &s2));
}
#[test]
fn prune_missing_drops_absent_ids() {
let mut r = ImeRegistry::new();
r.register(id(1));
r.register(id(2));
r.register(id(3));
let alive: HashSet<ElementId> = [id(1), id(3)].into_iter().collect();
r.prune_missing(&alive);
assert_eq!(r.len(), 2);
assert!(r.get(id(1)).is_some());
assert!(r.get(id(2)).is_none());
assert!(r.get(id(3)).is_some());
}
#[test]
fn undo_history_survives_re_registration() {
use crate::elements::text_edit::undo::{EditOp, EditSnapshot};
let mut r = ImeRegistry::new();
let s1 = r.register(id(1));
{
let mut state = s1.borrow_mut();
state.text = "a".to_string();
state.caret = 1;
state.undo.record_edit(
EditOp::Insert,
EditSnapshot {
text: "a".to_string(),
caret: 1,
anchor: None,
},
);
}
let s2 = r.register(id(1));
assert!(Rc::ptr_eq(&s1, &s2));
let restored = s2.borrow_mut().undo.undo();
assert_eq!(restored.map(|s| s.text), Some(String::new()));
}
}