1use crate::root::WindowState;
2use gpui::{App, Entity, FocusHandle, Focusable as _, SharedString, Window};
3use gpui_base::OtpState;
4use ropey::Rope;
5
6use super::{EditorState, InputState, TextareaState};
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 install_token_hover_presentation(
67 &self,
68 hover_listener: Option<gpui_base::input::InlineTokenHoverListener>,
69 cx: &mut App,
70 ) {
71 dispatch!(self, |state| state.update(cx, |state, _| state
72 .install_token_hover_presentation(hover_listener)))
73 }
74
75 pub(crate) fn entity_id(&self) -> gpui::EntityId {
76 dispatch!(self, |state| state.entity_id())
77 }
78
79 pub(crate) fn presentation(&self, cx: &App) -> gpui_base::input::InputPresentation {
80 dispatch!(self, |state| state.read(cx).presentation())
81 }
82
83 pub(crate) fn text<'a>(&self, cx: &'a App) -> &'a Rope {
87 dispatch!(self, |state| state.read(cx).text())
88 }
89
90 pub(crate) fn focus(&self, window: &mut Window, cx: &mut App) {
91 dispatch!(self, |state| state
92 .update(cx, |state, cx| state.focus(window, cx)))
93 }
94
95 pub(crate) fn clean(&self, window: &mut Window, cx: &mut App) {
96 dispatch!(self, |state| state
97 .update(cx, |state, cx| state.clean(window, cx)))
98 }
99
100 pub(crate) fn toggle_masked(&self, window: &mut Window, cx: &mut App) {
101 dispatch!(self, |state| state
102 .update(cx, |state, cx| state.toggle_masked(window, cx)))
103 }
104
105 pub(crate) fn set_text_align(&self, align: gpui::TextAlign, cx: &mut App) {
106 dispatch!(self, |state| state
107 .update(cx, |state, cx| state.set_text_align(align, cx)))
108 }
109
110 pub(crate) fn set_disabled(&self, disabled: bool, cx: &mut App) {
111 dispatch!(self, |state| state
112 .update(cx, |state, cx| state.set_disabled(disabled, cx)))
113 }
114
115 pub(crate) fn set_readonly(&self, readonly: bool, cx: &mut App) {
116 dispatch!(self, |state| state
117 .update(cx, |state, cx| state.set_readonly(readonly, cx)))
118 }
119
120 pub(crate) fn set_editor_style(&self, style: gpui_base::input::InputEditorStyle, cx: &mut App) {
121 dispatch!(self, |state| state
122 .update(cx, |state, _| state.set_editor_style(style)))
123 }
124
125 pub(crate) fn set_editor_paddings(&self, paddings: gpui::Edges<gpui::Pixels>, cx: &mut App) {
126 dispatch!(self, |state| state
127 .update(cx, |state, _| state.set_editor_paddings(paddings)))
128 }
129
130 pub(crate) fn ensure_highlighter_factory(
131 &self,
132 factory: gpui_base::input::InputHighlighterFactory,
133 cx: &mut App,
134 ) {
135 dispatch!(self, |state| state
136 .update(cx, |state, _| state.ensure_highlighter_factory(factory)))
137 }
138
139 pub(crate) fn on_context_menu(
140 &self,
141 handler: std::rc::Rc<
142 dyn Fn(
143 gpui_base::input::NativeMenu,
144 gpui_base::input::InputContextMenuCapabilities,
145 gpui::Point<gpui::Pixels>,
146 &mut Window,
147 &mut App,
148 ),
149 >,
150 cx: &mut App,
151 ) {
152 dispatch!(self, |state| state
153 .update(cx, |state, _| state.on_context_menu(handler)))
154 }
155
156 pub(crate) fn touch_selection(&self, cx: &App) -> Option<gpui_base::TouchSelectionSnapshot> {
158 dispatch!(self, |state| state.read(cx).touch_selection())
159 }
160
161 pub(crate) fn context_menu_capabilities(
162 &self,
163 cx: &App,
164 ) -> gpui_base::input::InputContextMenuCapabilities {
165 dispatch!(self, |state| state.read(cx).context_menu_capabilities())
166 }
167
168 pub(crate) fn is_all_selected(&self, cx: &App) -> bool {
171 dispatch!(self, |state| {
172 let state = state.read(cx);
173 state.selected_range() == (0..state.text().len())
174 })
175 }
176
177 pub(crate) fn begin_edge_drag(
178 &self,
179 edge: gpui_base::SelectionEdge,
180 finger: gpui::Point<gpui::Pixels>,
181 cx: &mut App,
182 ) {
183 dispatch!(self, |state| state
184 .update(cx, |state, cx| state.begin_edge_drag(edge, finger, cx)))
185 }
186
187 pub(crate) fn update_edge_drag(&self, finger: gpui::Point<gpui::Pixels>, cx: &mut App) {
188 dispatch!(self, |state| state
189 .update(cx, |state, cx| state.update_edge_drag(finger, cx)))
190 }
191
192 pub(crate) fn end_edge_drag(&self, cx: &mut App) {
193 dispatch!(self, |state| state
194 .update(cx, |state, cx| state.end_edge_drag(cx)))
195 }
196
197 pub(crate) fn close_edit_menu(&self, cx: &mut App) {
198 dispatch!(self, |state| state
199 .update(cx, |state, cx| state.close_edit_menu(cx)))
200 }
201
202 pub(crate) fn select_all_from_edit_menu(&self, window: &mut Window, cx: &mut App) {
203 dispatch!(self, |state| state.update(cx, |state, cx| state
204 .select_all_from_edit_menu(window, cx)))
205 }
206
207 pub(super) fn render_overlays(
209 &self,
210 window: &mut Window,
211 cx: &mut App,
212 ) -> super::overlay::InputOverlays {
213 dispatch!(self, |state| super::overlay::render_overlays(
214 state, window, cx
215 ))
216 }
217
218 pub(crate) fn replace_all(&self, value: String, window: &mut Window, cx: &mut App) {
219 dispatch!(self, |state| state
220 .update(cx, |state, cx| state.replace_all(value, window, cx)))
221 }
222
223 pub(crate) fn into_any_element(self) -> gpui::AnyElement {
225 use gpui::IntoElement as _;
226 dispatch!(self, |state| state.into_any_element())
227 }
228}
229
230impl From<&TextInputState> for AnyInputState {
231 fn from(state: &TextInputState) -> Self {
232 match state {
233 TextInputState::Input(state) => Self::Input(state.clone()),
234 TextInputState::Textarea(state) => Self::Textarea(state.clone()),
235 TextInputState::Editor(state) => Self::Editor(state.clone()),
236 }
237 }
238}
239
240impl From<Entity<InputState>> for TextInputState {
241 fn from(state: Entity<InputState>) -> Self {
242 Self::Input(state)
243 }
244}
245
246impl From<Entity<TextareaState>> for TextInputState {
247 fn from(state: Entity<TextareaState>) -> Self {
248 Self::Textarea(state)
249 }
250}
251
252impl From<Entity<EditorState>> for TextInputState {
253 fn from(state: Entity<EditorState>) -> Self {
254 Self::Editor(state)
255 }
256}
257
258impl AnyInputState {
259 pub fn as_input(&self) -> Option<&Entity<InputState>> {
261 match self {
262 Self::Input(state) => Some(state),
263 _ => None,
264 }
265 }
266
267 pub fn as_textarea(&self) -> Option<&Entity<TextareaState>> {
269 match self {
270 Self::Textarea(state) => Some(state),
271 _ => None,
272 }
273 }
274
275 pub fn as_editor(&self) -> Option<&Entity<EditorState>> {
277 match self {
278 Self::Editor(state) => Some(state),
279 _ => None,
280 }
281 }
282
283 pub fn as_otp(&self) -> Option<&Entity<OtpState>> {
285 match self {
286 Self::Otp(state) => Some(state),
287 _ => None,
288 }
289 }
290
291 pub fn value(&self, cx: &App) -> SharedString {
295 match self {
296 Self::Input(state) => state.read(cx).value(),
297 Self::Textarea(state) => state.read(cx).value(),
298 Self::Editor(state) => state.read(cx).value(),
299 Self::Otp(state) => state.read(cx).value().clone(),
300 }
301 }
302
303 pub fn focus_handle(&self, cx: &App) -> FocusHandle {
305 match self {
306 Self::Input(state) => state.focus_handle(cx),
307 Self::Textarea(state) => state.focus_handle(cx),
308 Self::Editor(state) => state.focus_handle(cx),
309 Self::Otp(state) => state.focus_handle(cx),
310 }
311 }
312}
313
314impl From<Entity<InputState>> for AnyInputState {
315 fn from(state: Entity<InputState>) -> Self {
316 Self::Input(state)
317 }
318}
319
320impl From<Entity<TextareaState>> for AnyInputState {
321 fn from(state: Entity<TextareaState>) -> Self {
322 Self::Textarea(state)
323 }
324}
325
326impl From<Entity<EditorState>> for AnyInputState {
327 fn from(state: Entity<EditorState>) -> Self {
328 Self::Editor(state)
329 }
330}
331
332impl From<Entity<OtpState>> for AnyInputState {
333 fn from(state: Entity<OtpState>) -> Self {
334 Self::Otp(state)
335 }
336}
337
338pub(super) fn sync_focused_input_registry(
341 state: impl Into<AnyInputState>,
342 window: &mut Window,
343 cx: &mut App,
344) {
345 let state = state.into();
346 let focused = state.focus_handle(cx).is_focused(window);
347 WindowState::try_update(window, cx, |root, _, cx| {
348 if focused {
349 root.focused_input = Some(state.clone());
350 } else if root.focused_input.as_ref() == Some(&state) {
351 root.focused_input = None;
352 }
353 cx.notify();
354 });
355}