use std::collections::HashMap;
use std::sync::Arc;
use fret_core::{AppWindowId, Rect};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct WindowImeSurroundingText {
pub text: Arc<str>,
pub cursor: u32,
pub anchor: u32,
}
impl WindowImeSurroundingText {
pub const MAX_TEXT_BYTES: usize = 4000;
pub fn best_effort_for_str(text: &str, cursor: usize, anchor: usize) -> Self {
fn clamp_down_to_char_boundary(text: &str, idx: usize) -> usize {
let mut idx = idx.min(text.len());
while idx > 0 && !text.is_char_boundary(idx) {
idx = idx.saturating_sub(1);
}
idx
}
let cursor = clamp_down_to_char_boundary(text, cursor);
let mut anchor = clamp_down_to_char_boundary(text, anchor);
let len = text.len();
if len <= Self::MAX_TEXT_BYTES {
return Self {
text: Arc::<str>::from(text),
cursor: u32::try_from(cursor).unwrap_or(u32::MAX),
anchor: u32::try_from(anchor).unwrap_or(u32::MAX),
};
}
let mut low = cursor.min(anchor);
let mut high = cursor.max(anchor);
if high.saturating_sub(low) > Self::MAX_TEXT_BYTES {
anchor = cursor;
low = cursor;
high = cursor;
}
let needed = high.saturating_sub(low);
let slack = Self::MAX_TEXT_BYTES.saturating_sub(needed);
let before = slack / 2;
let mut start = low
.saturating_sub(before)
.min(len.saturating_sub(Self::MAX_TEXT_BYTES));
let mut end = (start + Self::MAX_TEXT_BYTES).min(len);
start = clamp_down_to_char_boundary(text, start);
end = clamp_down_to_char_boundary(text, end);
if end < start {
end = start;
}
let cursor_rel = cursor.saturating_sub(start).min(end.saturating_sub(start));
let anchor_rel = anchor.saturating_sub(start).min(end.saturating_sub(start));
let excerpt = &text[start..end];
Self {
text: Arc::<str>::from(excerpt),
cursor: u32::try_from(cursor_rel).unwrap_or(u32::MAX),
anchor: u32::try_from(anchor_rel).unwrap_or(u32::MAX),
}
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct WindowTextInputSnapshot {
pub focus_is_text_input: bool,
pub is_composing: bool,
pub text_len_utf16: u32,
pub selection_utf16: Option<(u32, u32)>,
pub marked_utf16: Option<(u32, u32)>,
pub ime_cursor_area: Option<Rect>,
pub surrounding_text: Option<WindowImeSurroundingText>,
}
#[derive(Debug, Default)]
pub struct WindowTextInputSnapshotService {
by_window: HashMap<AppWindowId, WindowTextInputSnapshot>,
}
impl WindowTextInputSnapshotService {
pub fn snapshot(&self, window: AppWindowId) -> Option<&WindowTextInputSnapshot> {
self.by_window.get(&window)
}
pub fn set_snapshot(&mut self, window: AppWindowId, snapshot: WindowTextInputSnapshot) {
self.by_window.insert(window, snapshot);
}
pub fn remove_window(&mut self, window: AppWindowId) {
self.by_window.remove(&window);
}
}