gpui_platform/
input_handler.rs1use crate::ClipboardItem;
4use gpui_types::{Bounds, Pixels, Point, px};
5use std::{any::Any, ops::Range};
6
7#[derive(Debug)]
10pub struct UTF16Selection {
11 pub range: Range<usize>,
14 pub reversed: bool,
17}
18
19#[expect(missing_docs)]
25pub trait PlatformInputHandlerDelegate: Any {
26 fn selected_text_range(&mut self, ignore_disabled_input: bool) -> Option<UTF16Selection>;
27 fn marked_text_range(&mut self) -> Option<Range<usize>>;
28 fn text_for_range(
29 &mut self,
30 range_utf16: Range<usize>,
31 adjusted: &mut Option<Range<usize>>,
32 ) -> Option<String>;
33 fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, text: &str);
34 fn replace_and_mark_text_in_range(
35 &mut self,
36 range_utf16: Option<Range<usize>>,
37 new_text: &str,
38 new_selected_range: Option<Range<usize>>,
39 );
40 fn unmark_text(&mut self);
41 fn paste(&mut self, item: ClipboardItem);
42 fn bounds_for_range(&mut self, range_utf16: Range<usize>) -> Option<Bounds<Pixels>>;
43 fn apple_press_and_hold_enabled(&mut self) -> bool;
44 fn character_index_for_point(&mut self, point: Point<Pixels>) -> Option<usize>;
45 fn set_selected_text_range(&mut self, range_utf16: Range<usize>);
46 fn element_bounds(&mut self) -> Option<Bounds<Pixels>>;
47 fn text_length_utf16(&mut self) -> Option<usize>;
48 fn query_accepts_text_input(&mut self) -> bool;
49 fn query_prefers_ime_for_printable_keys(&mut self) -> bool;
50 fn text_input_editable_range(&mut self) -> Option<Range<usize>>;
51 fn as_any_mut(&mut self) -> &mut dyn Any;
54}
55
56#[allow(dead_code)]
61pub struct PlatformInputHandler {
62 delegate: Box<dyn PlatformInputHandlerDelegate>,
63}
64
65#[allow(dead_code)]
66#[expect(missing_docs)]
67impl PlatformInputHandler {
68 pub fn from_delegate(delegate: Box<dyn PlatformInputHandlerDelegate>) -> Self {
70 Self { delegate }
71 }
72
73 pub fn delegate_as_any_mut(&mut self) -> &mut dyn Any {
76 self.delegate.as_any_mut()
77 }
78
79 pub fn selected_text_range(&mut self, ignore_disabled_input: bool) -> Option<UTF16Selection> {
80 self.delegate.selected_text_range(ignore_disabled_input)
81 }
82
83 pub fn marked_text_range(&mut self) -> Option<Range<usize>> {
84 self.delegate.marked_text_range()
85 }
86
87 pub fn text_for_range(
88 &mut self,
89 range_utf16: Range<usize>,
90 adjusted: &mut Option<Range<usize>>,
91 ) -> Option<String> {
92 self.delegate.text_for_range(range_utf16, adjusted)
93 }
94
95 pub fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, text: &str) {
96 self.delegate.replace_text_in_range(replacement_range, text);
97 }
98
99 pub fn replace_and_mark_text_in_range(
100 &mut self,
101 range_utf16: Option<Range<usize>>,
102 new_text: &str,
103 new_selected_range: Option<Range<usize>>,
104 ) {
105 self.delegate
106 .replace_and_mark_text_in_range(range_utf16, new_text, new_selected_range);
107 }
108
109 pub fn unmark_text(&mut self) {
110 self.delegate.unmark_text();
111 }
112
113 pub fn paste(&mut self, item: ClipboardItem) {
114 self.delegate.paste(item);
115 }
116
117 pub fn bounds_for_range(&mut self, range_utf16: Range<usize>) -> Option<Bounds<Pixels>> {
118 self.delegate.bounds_for_range(range_utf16)
119 }
120
121 pub fn apple_press_and_hold_enabled(&mut self) -> bool {
122 self.delegate.apple_press_and_hold_enabled()
123 }
124
125 pub fn character_index_for_point(&mut self, point: Point<Pixels>) -> Option<usize> {
126 self.delegate.character_index_for_point(point)
127 }
128
129 pub fn set_selected_text_range(&mut self, range_utf16: Range<usize>) {
130 self.delegate.set_selected_text_range(range_utf16);
131 }
132
133 pub fn element_bounds(&mut self) -> Option<Bounds<Pixels>> {
134 self.delegate.element_bounds()
135 }
136
137 pub fn text_length_utf16(&mut self) -> Option<usize> {
138 self.delegate.text_length_utf16()
139 }
140
141 pub fn query_accepts_text_input(&mut self) -> bool {
142 self.delegate.query_accepts_text_input()
143 }
144
145 pub fn query_prefers_ime_for_printable_keys(&mut self) -> bool {
146 self.delegate.query_prefers_ime_for_printable_keys()
147 }
148
149 pub fn text_input_editable_range(&mut self) -> Option<Range<usize>> {
150 self.delegate.text_input_editable_range()
151 }
152
153 pub fn compute_ime_candidate_bounds(
154 marked_range: Option<Range<usize>>,
155 selection: &UTF16Selection,
156 mut bounds_for_range: impl FnMut(Range<usize>) -> Option<Bounds<Pixels>>,
157 ) -> Option<Bounds<Pixels>> {
158 if let Some(marked_range) = marked_range {
159 let mut line_start = marked_range.start;
161
162 let caret = selection.range.end;
166 if let Some(caret_bounds) = bounds_for_range(caret..caret) {
167 for i in (marked_range.start..caret).rev() {
168 if let Some(b) = bounds_for_range(i..i) {
169 if (b.origin.y - caret_bounds.origin.y).abs() > px(0.1) {
170 line_start = i + 1;
171 break;
172 }
173 }
174 }
175 }
176 bounds_for_range(line_start..line_start)
177 } else {
178 let offset = if selection.reversed {
180 selection.range.start
181 } else {
182 selection.range.end
183 };
184 bounds_for_range(offset..offset)
185 }
186 }
187
188 pub fn ime_candidate_bounds(&mut self) -> Option<Bounds<Pixels>> {
189 let marked_range = self.marked_text_range();
190 let selection = self.selected_text_range(true)?;
191 Self::compute_ime_candidate_bounds(marked_range, &selection, |range| {
192 self.bounds_for_range(range)
193 })
194 }
195}