1use gpui::{App, Entity, FocusHandle, Focusable as _, SharedString, Window};
2use gpui_base::OtpState;
3use ropey::Rope;
4
5use super::{EditorState, InputState, TextareaState};
6use crate::Root;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
17pub enum AnyInputState {
18 Input(Entity<InputState>),
20 Textarea(Entity<TextareaState>),
22 Editor(Entity<EditorState>),
24 Otp(Entity<OtpState>),
26}
27
28#[derive(Debug, Clone)]
37pub(crate) enum TextInputState {
38 Input(Entity<InputState>),
39 Textarea(Entity<TextareaState>),
40 Editor(Entity<EditorState>),
41}
42
43macro_rules! dispatch {
45 ($self:expr, |$state:ident| $body:expr) => {
46 match $self {
47 TextInputState::Input($state) => $body,
48 TextInputState::Textarea($state) => $body,
49 TextInputState::Editor($state) => $body,
50 }
51 };
52}
53
54impl TextInputState {
55 pub(crate) fn install_token_presentation(
56 &self,
57 renderer: Option<gpui_base::input::InlineTokenRenderer>,
58 listener: Option<gpui_base::input::InlineTokenClickListener>,
59 secret: bool,
60 cx: &mut App,
61 ) {
62 dispatch!(self, |state| state.update(cx, |state, _| state
63 .install_token_presentation(renderer, listener, secret)))
64 }
65
66 pub(crate) fn entity_id(&self) -> gpui::EntityId {
67 dispatch!(self, |state| state.entity_id())
68 }
69
70 pub(crate) fn presentation(&self, cx: &App) -> gpui_base::input::InputPresentation {
71 dispatch!(self, |state| state.read(cx).presentation())
72 }
73
74 pub(crate) fn text<'a>(&self, cx: &'a App) -> &'a Rope {
78 dispatch!(self, |state| state.read(cx).text())
79 }
80
81 pub(crate) fn focus(&self, window: &mut Window, cx: &mut App) {
82 dispatch!(self, |state| state
83 .update(cx, |state, cx| state.focus(window, cx)))
84 }
85
86 pub(crate) fn clean(&self, window: &mut Window, cx: &mut App) {
87 dispatch!(self, |state| state
88 .update(cx, |state, cx| state.clean(window, cx)))
89 }
90
91 pub(crate) fn toggle_masked(&self, window: &mut Window, cx: &mut App) {
92 dispatch!(self, |state| state
93 .update(cx, |state, cx| state.toggle_masked(window, cx)))
94 }
95
96 pub(crate) fn set_text_align(&self, align: gpui::TextAlign, cx: &mut App) {
97 dispatch!(self, |state| state
98 .update(cx, |state, cx| state.set_text_align(align, cx)))
99 }
100
101 pub(crate) fn set_disabled(&self, disabled: bool, cx: &mut App) {
102 dispatch!(self, |state| state
103 .update(cx, |state, cx| state.set_disabled(disabled, cx)))
104 }
105
106 pub(crate) fn set_readonly(&self, readonly: bool, cx: &mut App) {
107 dispatch!(self, |state| state
108 .update(cx, |state, cx| state.set_readonly(readonly, cx)))
109 }
110
111 pub(crate) fn set_editor_style(&self, style: gpui_base::input::InputEditorStyle, cx: &mut App) {
112 dispatch!(self, |state| state
113 .update(cx, |state, _| state.set_editor_style(style)))
114 }
115
116 pub(crate) fn set_editor_paddings(&self, paddings: gpui::Edges<gpui::Pixels>, cx: &mut App) {
117 dispatch!(self, |state| state
118 .update(cx, |state, _| state.set_editor_paddings(paddings)))
119 }
120
121 pub(crate) fn ensure_highlighter_factory(
122 &self,
123 factory: gpui_base::input::InputHighlighterFactory,
124 cx: &mut App,
125 ) {
126 dispatch!(self, |state| state
127 .update(cx, |state, _| state.ensure_highlighter_factory(factory)))
128 }
129
130 pub(crate) fn on_context_menu(
131 &self,
132 handler: std::rc::Rc<
133 dyn Fn(
134 gpui_base::input::NativeMenu,
135 gpui_base::input::InputContextMenuCapabilities,
136 gpui::Point<gpui::Pixels>,
137 &mut Window,
138 &mut App,
139 ),
140 >,
141 cx: &mut App,
142 ) {
143 dispatch!(self, |state| state
144 .update(cx, |state, _| state.on_context_menu(handler)))
145 }
146
147 pub(crate) fn touch_selection(&self, cx: &App) -> Option<gpui_base::TouchSelectionSnapshot> {
149 dispatch!(self, |state| state.read(cx).touch_selection())
150 }
151
152 pub(crate) fn context_menu_capabilities(
153 &self,
154 cx: &App,
155 ) -> gpui_base::input::InputContextMenuCapabilities {
156 dispatch!(self, |state| state.read(cx).context_menu_capabilities())
157 }
158
159 pub(crate) fn is_all_selected(&self, cx: &App) -> bool {
162 dispatch!(self, |state| {
163 let state = state.read(cx);
164 state.selected_range() == (0..state.text().len())
165 })
166 }
167
168 pub(crate) fn begin_edge_drag(
169 &self,
170 edge: gpui_base::SelectionEdge,
171 finger: gpui::Point<gpui::Pixels>,
172 cx: &mut App,
173 ) {
174 dispatch!(self, |state| state
175 .update(cx, |state, cx| state.begin_edge_drag(edge, finger, cx)))
176 }
177
178 pub(crate) fn update_edge_drag(&self, finger: gpui::Point<gpui::Pixels>, cx: &mut App) {
179 dispatch!(self, |state| state
180 .update(cx, |state, cx| state.update_edge_drag(finger, cx)))
181 }
182
183 pub(crate) fn end_edge_drag(&self, cx: &mut App) {
184 dispatch!(self, |state| state
185 .update(cx, |state, cx| state.end_edge_drag(cx)))
186 }
187
188 pub(crate) fn close_edit_menu(&self, cx: &mut App) {
189 dispatch!(self, |state| state
190 .update(cx, |state, cx| state.close_edit_menu(cx)))
191 }
192
193 pub(crate) fn select_all_from_edit_menu(&self, window: &mut Window, cx: &mut App) {
194 dispatch!(self, |state| state.update(cx, |state, cx| state
195 .select_all_from_edit_menu(window, cx)))
196 }
197
198 pub(super) fn render_overlays(
200 &self,
201 window: &mut Window,
202 cx: &mut App,
203 ) -> super::overlay::InputOverlays {
204 dispatch!(self, |state| super::overlay::render_overlays(
205 state, window, cx
206 ))
207 }
208
209 pub(crate) fn replace_all(&self, value: String, window: &mut Window, cx: &mut App) {
210 dispatch!(self, |state| state
211 .update(cx, |state, cx| state.replace_all(value, window, cx)))
212 }
213
214 pub(crate) fn into_any_element(self) -> gpui::AnyElement {
216 use gpui::IntoElement as _;
217 dispatch!(self, |state| state.into_any_element())
218 }
219}
220
221impl From<&TextInputState> for AnyInputState {
222 fn from(state: &TextInputState) -> Self {
223 match state {
224 TextInputState::Input(state) => Self::Input(state.clone()),
225 TextInputState::Textarea(state) => Self::Textarea(state.clone()),
226 TextInputState::Editor(state) => Self::Editor(state.clone()),
227 }
228 }
229}
230
231impl From<Entity<InputState>> for TextInputState {
232 fn from(state: Entity<InputState>) -> Self {
233 Self::Input(state)
234 }
235}
236
237impl From<Entity<TextareaState>> for TextInputState {
238 fn from(state: Entity<TextareaState>) -> Self {
239 Self::Textarea(state)
240 }
241}
242
243impl From<Entity<EditorState>> for TextInputState {
244 fn from(state: Entity<EditorState>) -> Self {
245 Self::Editor(state)
246 }
247}
248
249impl AnyInputState {
250 pub fn as_input(&self) -> Option<&Entity<InputState>> {
252 match self {
253 Self::Input(state) => Some(state),
254 _ => None,
255 }
256 }
257
258 pub fn as_textarea(&self) -> Option<&Entity<TextareaState>> {
260 match self {
261 Self::Textarea(state) => Some(state),
262 _ => None,
263 }
264 }
265
266 pub fn as_editor(&self) -> Option<&Entity<EditorState>> {
268 match self {
269 Self::Editor(state) => Some(state),
270 _ => None,
271 }
272 }
273
274 pub fn as_otp(&self) -> Option<&Entity<OtpState>> {
276 match self {
277 Self::Otp(state) => Some(state),
278 _ => None,
279 }
280 }
281
282 pub fn value(&self, cx: &App) -> SharedString {
286 match self {
287 Self::Input(state) => state.read(cx).value(),
288 Self::Textarea(state) => state.read(cx).value(),
289 Self::Editor(state) => state.read(cx).value(),
290 Self::Otp(state) => state.read(cx).value().clone(),
291 }
292 }
293
294 pub fn focus_handle(&self, cx: &App) -> FocusHandle {
296 match self {
297 Self::Input(state) => state.focus_handle(cx),
298 Self::Textarea(state) => state.focus_handle(cx),
299 Self::Editor(state) => state.focus_handle(cx),
300 Self::Otp(state) => state.focus_handle(cx),
301 }
302 }
303}
304
305impl From<Entity<InputState>> for AnyInputState {
306 fn from(state: Entity<InputState>) -> Self {
307 Self::Input(state)
308 }
309}
310
311impl From<Entity<TextareaState>> for AnyInputState {
312 fn from(state: Entity<TextareaState>) -> Self {
313 Self::Textarea(state)
314 }
315}
316
317impl From<Entity<EditorState>> for AnyInputState {
318 fn from(state: Entity<EditorState>) -> Self {
319 Self::Editor(state)
320 }
321}
322
323impl From<Entity<OtpState>> for AnyInputState {
324 fn from(state: Entity<OtpState>) -> Self {
325 Self::Otp(state)
326 }
327}
328
329pub(super) fn sync_focused_input_registry(
332 state: impl Into<AnyInputState>,
333 window: &mut Window,
334 cx: &mut App,
335) {
336 let state = state.into();
337 let focused = state.focus_handle(cx).is_focused(window);
338 Root::try_update(window, cx, |root, _, cx| {
339 if focused {
340 root.focused_input = Some(state.clone());
341 } else if root.focused_input.as_ref() == Some(&state) {
342 root.focused_input = None;
343 }
344 cx.notify();
345 });
346}