use std::ops::Range; use gpui::{Bounds, Context, EntityInputHandler, Pixels, Point, UTF16Selection, Window}; use super::{ TextInput, edit::{from_utf16, to_utf16}, }; impl TextInput { fn range_from_utf16(&self, range: &Range) -> Range { from_utf16(&self.text, range.start)..from_utf16(&self.text, range.end) } fn range_to_utf16(&self, range: &Range) -> Range { to_utf16(&self.text, range.start)..to_utf16(&self.text, range.end) } } impl EntityInputHandler for TextInput { fn text_for_range( &mut self, range_utf16: Range, actual_range: &mut Option>, _: &mut Window, _: &mut Context, ) -> Option { let range = self.range_from_utf16(&range_utf16); actual_range.replace(self.range_to_utf16(&range)); Some(self.text[range].to_string()) } fn selected_text_range( &mut self, _: bool, _: &mut Window, _: &mut Context, ) -> Option { Some(UTF16Selection { range: self.range_to_utf16(&self.selection), reversed: self.reversed, }) } fn marked_text_range(&self, _: &mut Window, _: &mut Context) -> Option> { self.marked.as_ref().map(|range| self.range_to_utf16(range)) } fn unmark_text(&mut self, _: &mut Window, cx: &mut Context) { let Some(marked) = self.marked.clone() else { return; }; let composed = self.text[marked.clone()].to_string(); self.replace(marked, &composed, true, cx); } fn replace_text_in_range( &mut self, range_utf16: Option>, text: &str, _: &mut Window, cx: &mut Context, ) { log::debug!( "text input {:?}: replace {range_utf16:?} marked {:?} selection {:?} in {} bytes", cx.entity_id(), self.marked, self.selection, self.text.len() ); let range = range_utf16 .map(|range| self.range_from_utf16(&range)) .or(self.marked.clone()) .unwrap_or(self.selection.clone()); self.replace(range, text, true, cx); } fn replace_and_mark_text_in_range( &mut self, range_utf16: Option>, text: &str, selected_utf16: Option>, _: &mut Window, cx: &mut Context, ) { if self.disabled { return; } let range = range_utf16 .map(|range| self.range_from_utf16(&range)) .or(self.marked.clone()) .unwrap_or(self.selection.clone()); if self.marked.is_none() { self.composing = Some(self.snapshot()); } self.text.replace_range(range.clone(), text); self.marked = (!text.is_empty()).then(|| range.start..range.start + text.len()); if self.marked.is_none() { self.composing = None; } self.selection = match selected_utf16 { Some(inner) => { let inner = from_utf16(text, inner.start)..from_utf16(text, inner.end); range.start + inner.start..range.start + inner.end } None => range.start + text.len()..range.start + text.len(), }; self.reversed = false; cx.notify(); } fn bounds_for_range( &mut self, range_utf16: Range, bounds: Bounds, _: &mut Window, _: &mut Context, ) -> Option> { let range = self.range_from_utf16(&range_utf16); let line_height = self.layout.as_ref()?.line_height; let start = self.position_for(self.display_offset(range.start))?; let end = self.position_for(self.display_offset(range.end))?; let origin = bounds.origin - self.scroll; Some(Bounds::from_corners( origin + start, origin + gpui::point(end.x, end.y + line_height), )) } fn character_index_for_point( &mut self, point: Point, _: &mut Window, _: &mut Context, ) -> Option { self.layout.as_ref()?; Some(to_utf16(&self.text, self.offset_for_point(point))) } }