iced_widget/
text_input.rs

1//! Text inputs display fields that can be filled with text.
2//!
3//! # Example
4//! ```no_run
5//! # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; }
6//! # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
7//! #
8//! use iced::widget::text_input;
9//!
10//! struct State {
11//!    content: String,
12//! }
13//!
14//! #[derive(Debug, Clone)]
15//! enum Message {
16//!     ContentChanged(String)
17//! }
18//!
19//! fn view(state: &State) -> Element<'_, Message> {
20//!     text_input("Type something here...", &state.content)
21//!         .on_input(Message::ContentChanged)
22//!         .into()
23//! }
24//!
25//! fn update(state: &mut State, message: Message) {
26//!     match message {
27//!         Message::ContentChanged(content) => {
28//!             state.content = content;
29//!         }
30//!     }
31//! }
32//! ```
33mod editor;
34mod value;
35
36pub mod cursor;
37
38pub use cursor::Cursor;
39pub use value::Value;
40
41use editor::Editor;
42
43use crate::core::alignment;
44use crate::core::clipboard::{self, Clipboard};
45use crate::core::input_method;
46use crate::core::keyboard;
47use crate::core::keyboard::key;
48use crate::core::layout;
49use crate::core::mouse::{self, click};
50use crate::core::renderer;
51use crate::core::text::paragraph::{self, Paragraph as _};
52use crate::core::text::{self, Text};
53use crate::core::time::{Duration, Instant};
54use crate::core::touch;
55use crate::core::widget;
56use crate::core::widget::operation::{self, Operation};
57use crate::core::widget::tree::{self, Tree};
58use crate::core::window;
59use crate::core::{
60    Background, Border, Color, Element, Event, InputMethod, Layout, Length,
61    Padding, Pixels, Point, Rectangle, Shell, Size, Theme, Vector, Widget,
62};
63use crate::runtime::Action;
64use crate::runtime::task::{self, Task};
65
66/// A field that can be filled with text.
67///
68/// # Example
69/// ```no_run
70/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; }
71/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
72/// #
73/// use iced::widget::text_input;
74///
75/// struct State {
76///    content: String,
77/// }
78///
79/// #[derive(Debug, Clone)]
80/// enum Message {
81///     ContentChanged(String)
82/// }
83///
84/// fn view(state: &State) -> Element<'_, Message> {
85///     text_input("Type something here...", &state.content)
86///         .on_input(Message::ContentChanged)
87///         .into()
88/// }
89///
90/// fn update(state: &mut State, message: Message) {
91///     match message {
92///         Message::ContentChanged(content) => {
93///             state.content = content;
94///         }
95///     }
96/// }
97/// ```
98#[allow(missing_debug_implementations)]
99pub struct TextInput<
100    'a,
101    Message,
102    Theme = crate::Theme,
103    Renderer = crate::Renderer,
104> where
105    Theme: Catalog,
106    Renderer: text::Renderer,
107{
108    id: Option<Id>,
109    placeholder: String,
110    value: Value,
111    is_secure: bool,
112    font: Option<Renderer::Font>,
113    width: Length,
114    padding: Padding,
115    size: Option<Pixels>,
116    line_height: text::LineHeight,
117    alignment: alignment::Horizontal,
118    on_input: Option<Box<dyn Fn(String) -> Message + 'a>>,
119    on_paste: Option<Box<dyn Fn(String) -> Message + 'a>>,
120    on_submit: Option<Message>,
121    icon: Option<Icon<Renderer::Font>>,
122    class: Theme::Class<'a>,
123    last_status: Option<Status>,
124}
125
126/// The default [`Padding`] of a [`TextInput`].
127pub const DEFAULT_PADDING: Padding = Padding::new(5.0);
128
129impl<'a, Message, Theme, Renderer> TextInput<'a, Message, Theme, Renderer>
130where
131    Message: Clone,
132    Theme: Catalog,
133    Renderer: text::Renderer,
134{
135    /// Creates a new [`TextInput`] with the given placeholder and
136    /// its current value.
137    pub fn new(placeholder: &str, value: &str) -> Self {
138        TextInput {
139            id: None,
140            placeholder: String::from(placeholder),
141            value: Value::new(value),
142            is_secure: false,
143            font: None,
144            width: Length::Fill,
145            padding: DEFAULT_PADDING,
146            size: None,
147            line_height: text::LineHeight::default(),
148            alignment: alignment::Horizontal::Left,
149            on_input: None,
150            on_paste: None,
151            on_submit: None,
152            icon: None,
153            class: Theme::default(),
154            last_status: None,
155        }
156    }
157
158    /// Sets the [`Id`] of the [`TextInput`].
159    pub fn id(mut self, id: impl Into<Id>) -> Self {
160        self.id = Some(id.into());
161        self
162    }
163
164    /// Converts the [`TextInput`] into a secure password input.
165    pub fn secure(mut self, is_secure: bool) -> Self {
166        self.is_secure = is_secure;
167        self
168    }
169
170    /// Sets the message that should be produced when some text is typed into
171    /// the [`TextInput`].
172    ///
173    /// If this method is not called, the [`TextInput`] will be disabled.
174    pub fn on_input(
175        mut self,
176        on_input: impl Fn(String) -> Message + 'a,
177    ) -> Self {
178        self.on_input = Some(Box::new(on_input));
179        self
180    }
181
182    /// Sets the message that should be produced when some text is typed into
183    /// the [`TextInput`], if `Some`.
184    ///
185    /// If `None`, the [`TextInput`] will be disabled.
186    pub fn on_input_maybe(
187        mut self,
188        on_input: Option<impl Fn(String) -> Message + 'a>,
189    ) -> Self {
190        self.on_input = on_input.map(|f| Box::new(f) as _);
191        self
192    }
193
194    /// Sets the message that should be produced when the [`TextInput`] is
195    /// focused and the enter key is pressed.
196    pub fn on_submit(mut self, message: Message) -> Self {
197        self.on_submit = Some(message);
198        self
199    }
200
201    /// Sets the message that should be produced when the [`TextInput`] is
202    /// focused and the enter key is pressed, if `Some`.
203    pub fn on_submit_maybe(mut self, on_submit: Option<Message>) -> Self {
204        self.on_submit = on_submit;
205        self
206    }
207
208    /// Sets the message that should be produced when some text is pasted into
209    /// the [`TextInput`].
210    pub fn on_paste(
211        mut self,
212        on_paste: impl Fn(String) -> Message + 'a,
213    ) -> Self {
214        self.on_paste = Some(Box::new(on_paste));
215        self
216    }
217
218    /// Sets the message that should be produced when some text is pasted into
219    /// the [`TextInput`], if `Some`.
220    pub fn on_paste_maybe(
221        mut self,
222        on_paste: Option<impl Fn(String) -> Message + 'a>,
223    ) -> Self {
224        self.on_paste = on_paste.map(|f| Box::new(f) as _);
225        self
226    }
227
228    /// Sets the [`Font`] of the [`TextInput`].
229    ///
230    /// [`Font`]: text::Renderer::Font
231    pub fn font(mut self, font: Renderer::Font) -> Self {
232        self.font = Some(font);
233        self
234    }
235
236    /// Sets the [`Icon`] of the [`TextInput`].
237    pub fn icon(mut self, icon: Icon<Renderer::Font>) -> Self {
238        self.icon = Some(icon);
239        self
240    }
241
242    /// Sets the width of the [`TextInput`].
243    pub fn width(mut self, width: impl Into<Length>) -> Self {
244        self.width = width.into();
245        self
246    }
247
248    /// Sets the [`Padding`] of the [`TextInput`].
249    pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
250        self.padding = padding.into();
251        self
252    }
253
254    /// Sets the text size of the [`TextInput`].
255    pub fn size(mut self, size: impl Into<Pixels>) -> Self {
256        self.size = Some(size.into());
257        self
258    }
259
260    /// Sets the [`text::LineHeight`] of the [`TextInput`].
261    pub fn line_height(
262        mut self,
263        line_height: impl Into<text::LineHeight>,
264    ) -> Self {
265        self.line_height = line_height.into();
266        self
267    }
268
269    /// Sets the horizontal alignment of the [`TextInput`].
270    pub fn align_x(
271        mut self,
272        alignment: impl Into<alignment::Horizontal>,
273    ) -> Self {
274        self.alignment = alignment.into();
275        self
276    }
277
278    /// Sets the style of the [`TextInput`].
279    #[must_use]
280    pub fn style(mut self, style: impl Fn(&Theme, Status) -> Style + 'a) -> Self
281    where
282        Theme::Class<'a>: From<StyleFn<'a, Theme>>,
283    {
284        self.class = (Box::new(style) as StyleFn<'a, Theme>).into();
285        self
286    }
287
288    /// Sets the style class of the [`TextInput`].
289    #[must_use]
290    pub fn class(mut self, class: impl Into<Theme::Class<'a>>) -> Self {
291        self.class = class.into();
292        self
293    }
294
295    /// Lays out the [`TextInput`], overriding its [`Value`] if provided.
296    ///
297    /// [`Renderer`]: text::Renderer
298    pub fn layout(
299        &self,
300        tree: &mut Tree,
301        renderer: &Renderer,
302        limits: &layout::Limits,
303        value: Option<&Value>,
304    ) -> layout::Node {
305        let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>();
306        let value = value.unwrap_or(&self.value);
307
308        let font = self.font.unwrap_or_else(|| renderer.default_font());
309        let text_size = self.size.unwrap_or_else(|| renderer.default_size());
310        let padding = self.padding.fit(Size::ZERO, limits.max());
311        let height = self.line_height.to_absolute(text_size);
312
313        let limits = limits.width(self.width).shrink(padding);
314        let text_bounds = limits.resolve(self.width, height, Size::ZERO);
315
316        let placeholder_text = Text {
317            font,
318            line_height: self.line_height,
319            content: self.placeholder.as_str(),
320            bounds: Size::new(f32::INFINITY, text_bounds.height),
321            size: text_size,
322            align_x: text::Alignment::Default,
323            align_y: alignment::Vertical::Center,
324            shaping: text::Shaping::Advanced,
325            wrapping: text::Wrapping::default(),
326        };
327
328        state.placeholder.update(placeholder_text);
329
330        let secure_value = self.is_secure.then(|| value.secure());
331        let value = secure_value.as_ref().unwrap_or(value);
332
333        state.value.update(Text {
334            content: &value.to_string(),
335            ..placeholder_text
336        });
337
338        if let Some(icon) = &self.icon {
339            let mut content = [0; 4];
340
341            let icon_text = Text {
342                line_height: self.line_height,
343                content: icon.code_point.encode_utf8(&mut content) as &_,
344                font: icon.font,
345                size: icon.size.unwrap_or_else(|| renderer.default_size()),
346                bounds: Size::new(f32::INFINITY, text_bounds.height),
347                align_x: text::Alignment::Center,
348                align_y: alignment::Vertical::Center,
349                shaping: text::Shaping::Advanced,
350                wrapping: text::Wrapping::default(),
351            };
352
353            state.icon.update(icon_text);
354
355            let icon_width = state.icon.min_width();
356
357            let (text_position, icon_position) = match icon.side {
358                Side::Left => (
359                    Point::new(
360                        padding.left + icon_width + icon.spacing,
361                        padding.top,
362                    ),
363                    Point::new(padding.left, padding.top),
364                ),
365                Side::Right => (
366                    Point::new(padding.left, padding.top),
367                    Point::new(
368                        padding.left + text_bounds.width - icon_width,
369                        padding.top,
370                    ),
371                ),
372            };
373
374            let text_node = layout::Node::new(
375                text_bounds - Size::new(icon_width + icon.spacing, 0.0),
376            )
377            .move_to(text_position);
378
379            let icon_node =
380                layout::Node::new(Size::new(icon_width, text_bounds.height))
381                    .move_to(icon_position);
382
383            layout::Node::with_children(
384                text_bounds.expand(padding),
385                vec![text_node, icon_node],
386            )
387        } else {
388            let text = layout::Node::new(text_bounds)
389                .move_to(Point::new(padding.left, padding.top));
390
391            layout::Node::with_children(text_bounds.expand(padding), vec![text])
392        }
393    }
394
395    fn input_method<'b>(
396        &self,
397        state: &'b State<Renderer::Paragraph>,
398        layout: Layout<'_>,
399        value: &Value,
400    ) -> InputMethod<&'b str> {
401        let Some(Focus {
402            is_window_focused: true,
403            ..
404        }) = &state.is_focused
405        else {
406            return InputMethod::Disabled;
407        };
408
409        let secure_value = self.is_secure.then(|| value.secure());
410        let value = secure_value.as_ref().unwrap_or(value);
411
412        let text_bounds = layout.children().next().unwrap().bounds();
413
414        let caret_index = match state.cursor.state(value) {
415            cursor::State::Index(position) => position,
416            cursor::State::Selection { start, end } => start.min(end),
417        };
418
419        let text = state.value.raw();
420        let (cursor_x, scroll_offset) =
421            measure_cursor_and_scroll_offset(text, text_bounds, caret_index);
422
423        let alignment_offset = alignment_offset(
424            text_bounds.width,
425            text.min_width(),
426            self.alignment,
427        );
428
429        let x = (text_bounds.x + cursor_x).floor() - scroll_offset
430            + alignment_offset;
431
432        InputMethod::Enabled {
433            position: Point::new(x, text_bounds.y + text_bounds.height),
434            purpose: if self.is_secure {
435                input_method::Purpose::Secure
436            } else {
437                input_method::Purpose::Normal
438            },
439            preedit: state.preedit.as_ref().map(input_method::Preedit::as_ref),
440        }
441    }
442
443    /// Draws the [`TextInput`] with the given [`Renderer`], overriding its
444    /// [`Value`] if provided.
445    ///
446    /// [`Renderer`]: text::Renderer
447    pub fn draw(
448        &self,
449        tree: &Tree,
450        renderer: &mut Renderer,
451        theme: &Theme,
452        layout: Layout<'_>,
453        _cursor: mouse::Cursor,
454        value: Option<&Value>,
455        viewport: &Rectangle,
456    ) {
457        let state = tree.state.downcast_ref::<State<Renderer::Paragraph>>();
458        let value = value.unwrap_or(&self.value);
459        let is_disabled = self.on_input.is_none();
460
461        let secure_value = self.is_secure.then(|| value.secure());
462        let value = secure_value.as_ref().unwrap_or(value);
463
464        let bounds = layout.bounds();
465
466        let mut children_layout = layout.children();
467        let text_bounds = children_layout.next().unwrap().bounds();
468
469        let style = theme
470            .style(&self.class, self.last_status.unwrap_or(Status::Disabled));
471
472        renderer.fill_quad(
473            renderer::Quad {
474                bounds,
475                border: style.border,
476                ..renderer::Quad::default()
477            },
478            style.background,
479        );
480
481        if self.icon.is_some() {
482            let icon_layout = children_layout.next().unwrap();
483
484            renderer.fill_paragraph(
485                state.icon.raw(),
486                icon_layout.bounds().center(),
487                style.icon,
488                *viewport,
489            );
490        }
491
492        let text = value.to_string();
493
494        let (cursor, offset, is_selecting) = if let Some(focus) = state
495            .is_focused
496            .as_ref()
497            .filter(|focus| focus.is_window_focused)
498        {
499            match state.cursor.state(value) {
500                cursor::State::Index(position) => {
501                    let (text_value_width, offset) =
502                        measure_cursor_and_scroll_offset(
503                            state.value.raw(),
504                            text_bounds,
505                            position,
506                        );
507
508                    let is_cursor_visible = !is_disabled
509                        && ((focus.now - focus.updated_at).as_millis()
510                            / CURSOR_BLINK_INTERVAL_MILLIS)
511                            % 2
512                            == 0;
513
514                    let cursor = if is_cursor_visible {
515                        Some((
516                            renderer::Quad {
517                                bounds: Rectangle {
518                                    x: (text_bounds.x + text_value_width)
519                                        .floor(),
520                                    y: text_bounds.y,
521                                    width: 1.0,
522                                    height: text_bounds.height,
523                                },
524                                ..renderer::Quad::default()
525                            },
526                            style.value,
527                        ))
528                    } else {
529                        None
530                    };
531
532                    (cursor, offset, false)
533                }
534                cursor::State::Selection { start, end } => {
535                    let left = start.min(end);
536                    let right = end.max(start);
537
538                    let (left_position, left_offset) =
539                        measure_cursor_and_scroll_offset(
540                            state.value.raw(),
541                            text_bounds,
542                            left,
543                        );
544
545                    let (right_position, right_offset) =
546                        measure_cursor_and_scroll_offset(
547                            state.value.raw(),
548                            text_bounds,
549                            right,
550                        );
551
552                    let width = right_position - left_position;
553
554                    (
555                        Some((
556                            renderer::Quad {
557                                bounds: Rectangle {
558                                    x: text_bounds.x + left_position,
559                                    y: text_bounds.y,
560                                    width,
561                                    height: text_bounds.height,
562                                },
563                                ..renderer::Quad::default()
564                            },
565                            style.selection,
566                        )),
567                        if end == right {
568                            right_offset
569                        } else {
570                            left_offset
571                        },
572                        true,
573                    )
574                }
575            }
576        } else {
577            (None, 0.0, false)
578        };
579
580        let draw = |renderer: &mut Renderer, viewport| {
581            let paragraph = if text.is_empty()
582                && state
583                    .preedit
584                    .as_ref()
585                    .map(|preedit| preedit.content.is_empty())
586                    .unwrap_or(true)
587            {
588                state.placeholder.raw()
589            } else {
590                state.value.raw()
591            };
592
593            let alignment_offset = alignment_offset(
594                text_bounds.width,
595                paragraph.min_width(),
596                self.alignment,
597            );
598
599            if let Some((cursor, color)) = cursor {
600                renderer.with_translation(
601                    Vector::new(alignment_offset - offset, 0.0),
602                    |renderer| {
603                        renderer.fill_quad(cursor, color);
604                    },
605                );
606            } else {
607                renderer.with_translation(Vector::ZERO, |_| {});
608            }
609
610            renderer.fill_paragraph(
611                paragraph,
612                Point::new(text_bounds.x, text_bounds.center_y())
613                    + Vector::new(alignment_offset - offset, 0.0),
614                if text.is_empty() {
615                    style.placeholder
616                } else {
617                    style.value
618                },
619                viewport,
620            );
621        };
622
623        if is_selecting {
624            renderer
625                .with_layer(text_bounds, |renderer| draw(renderer, *viewport));
626        } else {
627            draw(renderer, text_bounds);
628        }
629    }
630}
631
632impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
633    for TextInput<'_, Message, Theme, Renderer>
634where
635    Message: Clone,
636    Theme: Catalog,
637    Renderer: text::Renderer,
638{
639    fn tag(&self) -> tree::Tag {
640        tree::Tag::of::<State<Renderer::Paragraph>>()
641    }
642
643    fn state(&self) -> tree::State {
644        tree::State::new(State::<Renderer::Paragraph>::new())
645    }
646
647    fn diff(&self, tree: &mut Tree) {
648        let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>();
649
650        // Stop pasting if input becomes disabled
651        if self.on_input.is_none() {
652            state.is_pasting = None;
653        }
654    }
655
656    fn size(&self) -> Size<Length> {
657        Size {
658            width: self.width,
659            height: Length::Shrink,
660        }
661    }
662
663    fn layout(
664        &self,
665        tree: &mut Tree,
666        renderer: &Renderer,
667        limits: &layout::Limits,
668    ) -> layout::Node {
669        self.layout(tree, renderer, limits, None)
670    }
671
672    fn operate(
673        &self,
674        tree: &mut Tree,
675        layout: Layout<'_>,
676        _renderer: &Renderer,
677        operation: &mut dyn Operation,
678    ) {
679        let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>();
680
681        operation.focusable(
682            self.id.as_ref().map(|id| &id.0),
683            layout.bounds(),
684            state,
685        );
686
687        operation.text_input(
688            self.id.as_ref().map(|id| &id.0),
689            layout.bounds(),
690            state,
691        );
692    }
693
694    fn update(
695        &mut self,
696        tree: &mut Tree,
697        event: &Event,
698        layout: Layout<'_>,
699        cursor: mouse::Cursor,
700        renderer: &Renderer,
701        clipboard: &mut dyn Clipboard,
702        shell: &mut Shell<'_, Message>,
703        _viewport: &Rectangle,
704    ) {
705        let update_cache = |state, value| {
706            replace_paragraph(
707                renderer,
708                state,
709                layout,
710                value,
711                self.font,
712                self.size,
713                self.line_height,
714            );
715        };
716
717        match &event {
718            Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
719            | Event::Touch(touch::Event::FingerPressed { .. }) => {
720                let state = state::<Renderer>(tree);
721                let cursor_before = state.cursor;
722
723                let click_position = cursor.position_over(layout.bounds());
724
725                state.is_focused = if click_position.is_some() {
726                    let now = Instant::now();
727
728                    Some(Focus {
729                        updated_at: now,
730                        now,
731                        is_window_focused: true,
732                    })
733                } else {
734                    None
735                };
736
737                if let Some(cursor_position) = click_position {
738                    let text_layout = layout.children().next().unwrap();
739
740                    let target = {
741                        let text_bounds = text_layout.bounds();
742
743                        let alignment_offset = alignment_offset(
744                            text_bounds.width,
745                            state.value.raw().min_width(),
746                            self.alignment,
747                        );
748
749                        cursor_position.x - text_bounds.x - alignment_offset
750                    };
751
752                    let click = mouse::Click::new(
753                        cursor_position,
754                        mouse::Button::Left,
755                        state.last_click,
756                    );
757
758                    match click.kind() {
759                        click::Kind::Single => {
760                            let position = if target > 0.0 {
761                                let value = if self.is_secure {
762                                    self.value.secure()
763                                } else {
764                                    self.value.clone()
765                                };
766
767                                find_cursor_position(
768                                    text_layout.bounds(),
769                                    &value,
770                                    state,
771                                    target,
772                                )
773                            } else {
774                                None
775                            }
776                            .unwrap_or(0);
777
778                            if state.keyboard_modifiers.shift() {
779                                state.cursor.select_range(
780                                    state.cursor.start(&self.value),
781                                    position,
782                                );
783                            } else {
784                                state.cursor.move_to(position);
785                            }
786                            state.is_dragging = true;
787                        }
788                        click::Kind::Double => {
789                            if self.is_secure {
790                                state.cursor.select_all(&self.value);
791                            } else {
792                                let position = find_cursor_position(
793                                    text_layout.bounds(),
794                                    &self.value,
795                                    state,
796                                    target,
797                                )
798                                .unwrap_or(0);
799
800                                state.cursor.select_range(
801                                    self.value.previous_start_of_word(position),
802                                    self.value.next_end_of_word(position),
803                                );
804                            }
805
806                            state.is_dragging = false;
807                        }
808                        click::Kind::Triple => {
809                            state.cursor.select_all(&self.value);
810                            state.is_dragging = false;
811                        }
812                    }
813
814                    state.last_click = Some(click);
815
816                    if cursor_before != state.cursor {
817                        shell.request_redraw();
818                    }
819
820                    shell.capture_event();
821                }
822            }
823            Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
824            | Event::Touch(touch::Event::FingerLifted { .. })
825            | Event::Touch(touch::Event::FingerLost { .. }) => {
826                state::<Renderer>(tree).is_dragging = false;
827            }
828            Event::Mouse(mouse::Event::CursorMoved { position })
829            | Event::Touch(touch::Event::FingerMoved { position, .. }) => {
830                let state = state::<Renderer>(tree);
831
832                if state.is_dragging {
833                    let text_layout = layout.children().next().unwrap();
834
835                    let target = {
836                        let text_bounds = text_layout.bounds();
837
838                        let alignment_offset = alignment_offset(
839                            text_bounds.width,
840                            state.value.raw().min_width(),
841                            self.alignment,
842                        );
843
844                        position.x - text_bounds.x - alignment_offset
845                    };
846
847                    let value = if self.is_secure {
848                        self.value.secure()
849                    } else {
850                        self.value.clone()
851                    };
852
853                    let position = find_cursor_position(
854                        text_layout.bounds(),
855                        &value,
856                        state,
857                        target,
858                    )
859                    .unwrap_or(0);
860
861                    let selection_before = state.cursor.selection(&value);
862
863                    state
864                        .cursor
865                        .select_range(state.cursor.start(&value), position);
866
867                    if let Some(focus) = &mut state.is_focused {
868                        focus.updated_at = Instant::now();
869                    }
870
871                    if selection_before != state.cursor.selection(&value) {
872                        shell.request_redraw();
873                    }
874
875                    shell.capture_event();
876                }
877            }
878            Event::Keyboard(keyboard::Event::KeyPressed {
879                key, text, ..
880            }) => {
881                let state = state::<Renderer>(tree);
882
883                if let Some(focus) = &mut state.is_focused {
884                    let modifiers = state.keyboard_modifiers;
885
886                    match key.as_ref() {
887                        keyboard::Key::Character("c")
888                            if state.keyboard_modifiers.command()
889                                && !self.is_secure =>
890                        {
891                            if let Some((start, end)) =
892                                state.cursor.selection(&self.value)
893                            {
894                                clipboard.write(
895                                    clipboard::Kind::Standard,
896                                    self.value.select(start, end).to_string(),
897                                );
898                            }
899
900                            shell.capture_event();
901                            return;
902                        }
903                        keyboard::Key::Character("x")
904                            if state.keyboard_modifiers.command()
905                                && !self.is_secure =>
906                        {
907                            let Some(on_input) = &self.on_input else {
908                                return;
909                            };
910
911                            if let Some((start, end)) =
912                                state.cursor.selection(&self.value)
913                            {
914                                clipboard.write(
915                                    clipboard::Kind::Standard,
916                                    self.value.select(start, end).to_string(),
917                                );
918                            }
919
920                            let mut editor =
921                                Editor::new(&mut self.value, &mut state.cursor);
922                            editor.delete();
923
924                            let message = (on_input)(editor.contents());
925                            shell.publish(message);
926                            shell.capture_event();
927
928                            focus.updated_at = Instant::now();
929                            update_cache(state, &self.value);
930                            return;
931                        }
932                        keyboard::Key::Character("v")
933                            if state.keyboard_modifiers.command()
934                                && !state.keyboard_modifiers.alt() =>
935                        {
936                            let Some(on_input) = &self.on_input else {
937                                return;
938                            };
939
940                            let content = match state.is_pasting.take() {
941                                Some(content) => content,
942                                None => {
943                                    let content: String = clipboard
944                                        .read(clipboard::Kind::Standard)
945                                        .unwrap_or_default()
946                                        .chars()
947                                        .filter(|c| !c.is_control())
948                                        .collect();
949
950                                    Value::new(&content)
951                                }
952                            };
953
954                            let mut editor =
955                                Editor::new(&mut self.value, &mut state.cursor);
956                            editor.paste(content.clone());
957
958                            let message = if let Some(paste) = &self.on_paste {
959                                (paste)(editor.contents())
960                            } else {
961                                (on_input)(editor.contents())
962                            };
963                            shell.publish(message);
964                            shell.capture_event();
965
966                            state.is_pasting = Some(content);
967                            focus.updated_at = Instant::now();
968                            update_cache(state, &self.value);
969                            return;
970                        }
971                        keyboard::Key::Character("a")
972                            if state.keyboard_modifiers.command() =>
973                        {
974                            let cursor_before = state.cursor;
975
976                            state.cursor.select_all(&self.value);
977
978                            if cursor_before != state.cursor {
979                                focus.updated_at = Instant::now();
980
981                                shell.request_redraw();
982                            }
983
984                            shell.capture_event();
985                            return;
986                        }
987                        _ => {}
988                    }
989
990                    if let Some(text) = text {
991                        let Some(on_input) = &self.on_input else {
992                            return;
993                        };
994
995                        state.is_pasting = None;
996
997                        if let Some(c) =
998                            text.chars().next().filter(|c| !c.is_control())
999                        {
1000                            let mut editor =
1001                                Editor::new(&mut self.value, &mut state.cursor);
1002
1003                            editor.insert(c);
1004
1005                            let message = (on_input)(editor.contents());
1006                            shell.publish(message);
1007                            shell.capture_event();
1008
1009                            focus.updated_at = Instant::now();
1010                            update_cache(state, &self.value);
1011                            return;
1012                        }
1013                    }
1014
1015                    match key.as_ref() {
1016                        keyboard::Key::Named(key::Named::Enter) => {
1017                            if let Some(on_submit) = self.on_submit.clone() {
1018                                shell.publish(on_submit);
1019                                shell.capture_event();
1020                            }
1021                        }
1022                        keyboard::Key::Named(key::Named::Backspace) => {
1023                            let Some(on_input) = &self.on_input else {
1024                                return;
1025                            };
1026
1027                            if modifiers.jump()
1028                                && state.cursor.selection(&self.value).is_none()
1029                            {
1030                                if self.is_secure {
1031                                    let cursor_pos =
1032                                        state.cursor.end(&self.value);
1033                                    state.cursor.select_range(0, cursor_pos);
1034                                } else {
1035                                    state
1036                                        .cursor
1037                                        .select_left_by_words(&self.value);
1038                                }
1039                            }
1040
1041                            let mut editor =
1042                                Editor::new(&mut self.value, &mut state.cursor);
1043                            editor.backspace();
1044
1045                            let message = (on_input)(editor.contents());
1046                            shell.publish(message);
1047                            shell.capture_event();
1048
1049                            focus.updated_at = Instant::now();
1050                            update_cache(state, &self.value);
1051                        }
1052                        keyboard::Key::Named(key::Named::Delete) => {
1053                            let Some(on_input) = &self.on_input else {
1054                                return;
1055                            };
1056
1057                            if modifiers.jump()
1058                                && state.cursor.selection(&self.value).is_none()
1059                            {
1060                                if self.is_secure {
1061                                    let cursor_pos =
1062                                        state.cursor.end(&self.value);
1063                                    state.cursor.select_range(
1064                                        cursor_pos,
1065                                        self.value.len(),
1066                                    );
1067                                } else {
1068                                    state
1069                                        .cursor
1070                                        .select_right_by_words(&self.value);
1071                                }
1072                            }
1073
1074                            let mut editor =
1075                                Editor::new(&mut self.value, &mut state.cursor);
1076                            editor.delete();
1077
1078                            let message = (on_input)(editor.contents());
1079                            shell.publish(message);
1080                            shell.capture_event();
1081
1082                            focus.updated_at = Instant::now();
1083                            update_cache(state, &self.value);
1084                        }
1085                        keyboard::Key::Named(key::Named::Home) => {
1086                            let cursor_before = state.cursor;
1087
1088                            if modifiers.shift() {
1089                                state.cursor.select_range(
1090                                    state.cursor.start(&self.value),
1091                                    0,
1092                                );
1093                            } else {
1094                                state.cursor.move_to(0);
1095                            }
1096
1097                            if cursor_before != state.cursor {
1098                                focus.updated_at = Instant::now();
1099
1100                                shell.request_redraw();
1101                            }
1102
1103                            shell.capture_event();
1104                        }
1105                        keyboard::Key::Named(key::Named::End) => {
1106                            let cursor_before = state.cursor;
1107
1108                            if modifiers.shift() {
1109                                state.cursor.select_range(
1110                                    state.cursor.start(&self.value),
1111                                    self.value.len(),
1112                                );
1113                            } else {
1114                                state.cursor.move_to(self.value.len());
1115                            }
1116
1117                            if cursor_before != state.cursor {
1118                                focus.updated_at = Instant::now();
1119
1120                                shell.request_redraw();
1121                            }
1122
1123                            shell.capture_event();
1124                        }
1125                        keyboard::Key::Named(key::Named::ArrowLeft)
1126                            if modifiers.macos_command() =>
1127                        {
1128                            let cursor_before = state.cursor;
1129
1130                            if modifiers.shift() {
1131                                state.cursor.select_range(
1132                                    state.cursor.start(&self.value),
1133                                    0,
1134                                );
1135                            } else {
1136                                state.cursor.move_to(0);
1137                            }
1138
1139                            if cursor_before != state.cursor {
1140                                focus.updated_at = Instant::now();
1141
1142                                shell.request_redraw();
1143                            }
1144
1145                            shell.capture_event();
1146                        }
1147                        keyboard::Key::Named(key::Named::ArrowRight)
1148                            if modifiers.macos_command() =>
1149                        {
1150                            let cursor_before = state.cursor;
1151
1152                            if modifiers.shift() {
1153                                state.cursor.select_range(
1154                                    state.cursor.start(&self.value),
1155                                    self.value.len(),
1156                                );
1157                            } else {
1158                                state.cursor.move_to(self.value.len());
1159                            }
1160
1161                            if cursor_before != state.cursor {
1162                                focus.updated_at = Instant::now();
1163
1164                                shell.request_redraw();
1165                            }
1166
1167                            shell.capture_event();
1168                        }
1169                        keyboard::Key::Named(key::Named::ArrowLeft) => {
1170                            let cursor_before = state.cursor;
1171
1172                            if modifiers.jump() && !self.is_secure {
1173                                if modifiers.shift() {
1174                                    state
1175                                        .cursor
1176                                        .select_left_by_words(&self.value);
1177                                } else {
1178                                    state
1179                                        .cursor
1180                                        .move_left_by_words(&self.value);
1181                                }
1182                            } else if modifiers.shift() {
1183                                state.cursor.select_left(&self.value);
1184                            } else {
1185                                state.cursor.move_left(&self.value);
1186                            }
1187
1188                            if cursor_before != state.cursor {
1189                                focus.updated_at = Instant::now();
1190
1191                                shell.request_redraw();
1192                            }
1193
1194                            shell.capture_event();
1195                        }
1196                        keyboard::Key::Named(key::Named::ArrowRight) => {
1197                            let cursor_before = state.cursor;
1198
1199                            if modifiers.jump() && !self.is_secure {
1200                                if modifiers.shift() {
1201                                    state
1202                                        .cursor
1203                                        .select_right_by_words(&self.value);
1204                                } else {
1205                                    state
1206                                        .cursor
1207                                        .move_right_by_words(&self.value);
1208                                }
1209                            } else if modifiers.shift() {
1210                                state.cursor.select_right(&self.value);
1211                            } else {
1212                                state.cursor.move_right(&self.value);
1213                            }
1214
1215                            if cursor_before != state.cursor {
1216                                focus.updated_at = Instant::now();
1217
1218                                shell.request_redraw();
1219                            }
1220
1221                            shell.capture_event();
1222                        }
1223                        keyboard::Key::Named(key::Named::Escape) => {
1224                            state.is_focused = None;
1225                            state.is_dragging = false;
1226                            state.is_pasting = None;
1227
1228                            state.keyboard_modifiers =
1229                                keyboard::Modifiers::default();
1230
1231                            shell.capture_event();
1232                        }
1233                        _ => {}
1234                    }
1235                }
1236            }
1237            Event::Keyboard(keyboard::Event::KeyReleased { key, .. }) => {
1238                let state = state::<Renderer>(tree);
1239
1240                if state.is_focused.is_some() {
1241                    if let keyboard::Key::Character("v") = key.as_ref() {
1242                        state.is_pasting = None;
1243
1244                        shell.capture_event();
1245                    }
1246                }
1247
1248                state.is_pasting = None;
1249            }
1250            Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => {
1251                let state = state::<Renderer>(tree);
1252
1253                state.keyboard_modifiers = *modifiers;
1254            }
1255            Event::InputMethod(event) => match event {
1256                input_method::Event::Opened | input_method::Event::Closed => {
1257                    let state = state::<Renderer>(tree);
1258
1259                    state.preedit =
1260                        matches!(event, input_method::Event::Opened)
1261                            .then(input_method::Preedit::new);
1262
1263                    shell.request_redraw();
1264                }
1265                input_method::Event::Preedit(content, selection) => {
1266                    let state = state::<Renderer>(tree);
1267
1268                    if state.is_focused.is_some() {
1269                        state.preedit = Some(input_method::Preedit {
1270                            content: content.to_owned(),
1271                            selection: selection.clone(),
1272                            text_size: self.size,
1273                        });
1274
1275                        shell.request_redraw();
1276                    }
1277                }
1278                input_method::Event::Commit(text) => {
1279                    let state = state::<Renderer>(tree);
1280
1281                    if let Some(focus) = &mut state.is_focused {
1282                        let Some(on_input) = &self.on_input else {
1283                            return;
1284                        };
1285
1286                        let mut editor =
1287                            Editor::new(&mut self.value, &mut state.cursor);
1288                        editor.paste(Value::new(text));
1289
1290                        focus.updated_at = Instant::now();
1291                        state.is_pasting = None;
1292
1293                        let message = (on_input)(editor.contents());
1294                        shell.publish(message);
1295                        shell.capture_event();
1296
1297                        update_cache(state, &self.value);
1298                    }
1299                }
1300            },
1301            Event::Window(window::Event::Unfocused) => {
1302                let state = state::<Renderer>(tree);
1303
1304                if let Some(focus) = &mut state.is_focused {
1305                    focus.is_window_focused = false;
1306                }
1307            }
1308            Event::Window(window::Event::Focused) => {
1309                let state = state::<Renderer>(tree);
1310
1311                if let Some(focus) = &mut state.is_focused {
1312                    focus.is_window_focused = true;
1313                    focus.updated_at = Instant::now();
1314
1315                    shell.request_redraw();
1316                }
1317            }
1318            Event::Window(window::Event::RedrawRequested(now)) => {
1319                let state = state::<Renderer>(tree);
1320
1321                if let Some(focus) = &mut state.is_focused {
1322                    if focus.is_window_focused {
1323                        if matches!(
1324                            state.cursor.state(&self.value),
1325                            cursor::State::Index(_)
1326                        ) {
1327                            focus.now = *now;
1328
1329                            let millis_until_redraw =
1330                                CURSOR_BLINK_INTERVAL_MILLIS
1331                                    - (*now - focus.updated_at).as_millis()
1332                                        % CURSOR_BLINK_INTERVAL_MILLIS;
1333
1334                            shell.request_redraw_at(
1335                                *now + Duration::from_millis(
1336                                    millis_until_redraw as u64,
1337                                ),
1338                            );
1339                        }
1340
1341                        shell.request_input_method(&self.input_method(
1342                            state,
1343                            layout,
1344                            &self.value,
1345                        ));
1346                    }
1347                }
1348            }
1349            _ => {}
1350        }
1351
1352        let state = state::<Renderer>(tree);
1353        let is_disabled = self.on_input.is_none();
1354
1355        let status = if is_disabled {
1356            Status::Disabled
1357        } else if state.is_focused() {
1358            Status::Focused {
1359                is_hovered: cursor.is_over(layout.bounds()),
1360            }
1361        } else if cursor.is_over(layout.bounds()) {
1362            Status::Hovered
1363        } else {
1364            Status::Active
1365        };
1366
1367        if let Event::Window(window::Event::RedrawRequested(_now)) = event {
1368            self.last_status = Some(status);
1369        } else if self
1370            .last_status
1371            .is_some_and(|last_status| status != last_status)
1372        {
1373            shell.request_redraw();
1374        }
1375    }
1376
1377    fn draw(
1378        &self,
1379        tree: &Tree,
1380        renderer: &mut Renderer,
1381        theme: &Theme,
1382        _style: &renderer::Style,
1383        layout: Layout<'_>,
1384        cursor: mouse::Cursor,
1385        viewport: &Rectangle,
1386    ) {
1387        self.draw(tree, renderer, theme, layout, cursor, None, viewport);
1388    }
1389
1390    fn mouse_interaction(
1391        &self,
1392        _state: &Tree,
1393        layout: Layout<'_>,
1394        cursor: mouse::Cursor,
1395        _viewport: &Rectangle,
1396        _renderer: &Renderer,
1397    ) -> mouse::Interaction {
1398        if cursor.is_over(layout.bounds()) {
1399            if self.on_input.is_none() {
1400                mouse::Interaction::Idle
1401            } else {
1402                mouse::Interaction::Text
1403            }
1404        } else {
1405            mouse::Interaction::default()
1406        }
1407    }
1408}
1409
1410impl<'a, Message, Theme, Renderer> From<TextInput<'a, Message, Theme, Renderer>>
1411    for Element<'a, Message, Theme, Renderer>
1412where
1413    Message: Clone + 'a,
1414    Theme: Catalog + 'a,
1415    Renderer: text::Renderer + 'a,
1416{
1417    fn from(
1418        text_input: TextInput<'a, Message, Theme, Renderer>,
1419    ) -> Element<'a, Message, Theme, Renderer> {
1420        Element::new(text_input)
1421    }
1422}
1423
1424/// The content of the [`Icon`].
1425#[derive(Debug, Clone)]
1426pub struct Icon<Font> {
1427    /// The font that will be used to display the `code_point`.
1428    pub font: Font,
1429    /// The unicode code point that will be used as the icon.
1430    pub code_point: char,
1431    /// The font size of the content.
1432    pub size: Option<Pixels>,
1433    /// The spacing between the [`Icon`] and the text in a [`TextInput`].
1434    pub spacing: f32,
1435    /// The side of a [`TextInput`] where to display the [`Icon`].
1436    pub side: Side,
1437}
1438
1439/// The side of a [`TextInput`].
1440#[derive(Debug, Clone)]
1441pub enum Side {
1442    /// The left side of a [`TextInput`].
1443    Left,
1444    /// The right side of a [`TextInput`].
1445    Right,
1446}
1447
1448/// The identifier of a [`TextInput`].
1449#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1450pub struct Id(widget::Id);
1451
1452impl Id {
1453    /// Creates a custom [`Id`].
1454    pub fn new(id: impl Into<std::borrow::Cow<'static, str>>) -> Self {
1455        Self(widget::Id::new(id))
1456    }
1457
1458    /// Creates a unique [`Id`].
1459    ///
1460    /// This function produces a different [`Id`] every time it is called.
1461    pub fn unique() -> Self {
1462        Self(widget::Id::unique())
1463    }
1464}
1465
1466impl From<Id> for widget::Id {
1467    fn from(id: Id) -> Self {
1468        id.0
1469    }
1470}
1471
1472impl From<&'static str> for Id {
1473    fn from(id: &'static str) -> Self {
1474        Self::new(id)
1475    }
1476}
1477
1478impl From<String> for Id {
1479    fn from(id: String) -> Self {
1480        Self::new(id)
1481    }
1482}
1483
1484/// Produces a [`Task`] that returns whether the [`TextInput`] with the given [`Id`] is focused or not.
1485pub fn is_focused(id: impl Into<Id>) -> Task<bool> {
1486    task::widget(operation::focusable::is_focused(id.into().into()))
1487}
1488
1489/// Produces a [`Task`] that focuses the [`TextInput`] with the given [`Id`].
1490pub fn focus<T>(id: impl Into<Id>) -> Task<T> {
1491    task::effect(Action::widget(operation::focusable::focus(id.into().0)))
1492}
1493
1494/// Produces a [`Task`] that moves the cursor of the [`TextInput`] with the given [`Id`] to the
1495/// end.
1496pub fn move_cursor_to_end<T>(id: impl Into<Id>) -> Task<T> {
1497    task::effect(Action::widget(operation::text_input::move_cursor_to_end(
1498        id.into().0,
1499    )))
1500}
1501
1502/// Produces a [`Task`] that moves the cursor of the [`TextInput`] with the given [`Id`] to the
1503/// front.
1504pub fn move_cursor_to_front<T>(id: impl Into<Id>) -> Task<T> {
1505    task::effect(Action::widget(operation::text_input::move_cursor_to_front(
1506        id.into().0,
1507    )))
1508}
1509
1510/// Produces a [`Task`] that moves the cursor of the [`TextInput`] with the given [`Id`] to the
1511/// provided position.
1512pub fn move_cursor_to<T>(id: impl Into<Id>, position: usize) -> Task<T> {
1513    task::effect(Action::widget(operation::text_input::move_cursor_to(
1514        id.into().0,
1515        position,
1516    )))
1517}
1518
1519/// Produces a [`Task`] that selects all the content of the [`TextInput`] with the given [`Id`].
1520pub fn select_all<T>(id: impl Into<Id>) -> Task<T> {
1521    task::effect(Action::widget(operation::text_input::select_all(
1522        id.into().0,
1523    )))
1524}
1525
1526/// The state of a [`TextInput`].
1527#[derive(Debug, Default, Clone)]
1528pub struct State<P: text::Paragraph> {
1529    value: paragraph::Plain<P>,
1530    placeholder: paragraph::Plain<P>,
1531    icon: paragraph::Plain<P>,
1532    is_focused: Option<Focus>,
1533    is_dragging: bool,
1534    is_pasting: Option<Value>,
1535    preedit: Option<input_method::Preedit>,
1536    last_click: Option<mouse::Click>,
1537    cursor: Cursor,
1538    keyboard_modifiers: keyboard::Modifiers,
1539    // TODO: Add stateful horizontal scrolling offset
1540}
1541
1542fn state<Renderer: text::Renderer>(
1543    tree: &mut Tree,
1544) -> &mut State<Renderer::Paragraph> {
1545    tree.state.downcast_mut::<State<Renderer::Paragraph>>()
1546}
1547
1548#[derive(Debug, Clone)]
1549struct Focus {
1550    updated_at: Instant,
1551    now: Instant,
1552    is_window_focused: bool,
1553}
1554
1555impl<P: text::Paragraph> State<P> {
1556    /// Creates a new [`State`], representing an unfocused [`TextInput`].
1557    pub fn new() -> Self {
1558        Self::default()
1559    }
1560
1561    /// Returns whether the [`TextInput`] is currently focused or not.
1562    pub fn is_focused(&self) -> bool {
1563        self.is_focused.is_some()
1564    }
1565
1566    /// Returns the [`Cursor`] of the [`TextInput`].
1567    pub fn cursor(&self) -> Cursor {
1568        self.cursor
1569    }
1570
1571    /// Focuses the [`TextInput`].
1572    pub fn focus(&mut self) {
1573        let now = Instant::now();
1574
1575        self.is_focused = Some(Focus {
1576            updated_at: now,
1577            now,
1578            is_window_focused: true,
1579        });
1580
1581        self.move_cursor_to_end();
1582    }
1583
1584    /// Unfocuses the [`TextInput`].
1585    pub fn unfocus(&mut self) {
1586        self.is_focused = None;
1587    }
1588
1589    /// Moves the [`Cursor`] of the [`TextInput`] to the front of the input text.
1590    pub fn move_cursor_to_front(&mut self) {
1591        self.cursor.move_to(0);
1592    }
1593
1594    /// Moves the [`Cursor`] of the [`TextInput`] to the end of the input text.
1595    pub fn move_cursor_to_end(&mut self) {
1596        self.cursor.move_to(usize::MAX);
1597    }
1598
1599    /// Moves the [`Cursor`] of the [`TextInput`] to an arbitrary location.
1600    pub fn move_cursor_to(&mut self, position: usize) {
1601        self.cursor.move_to(position);
1602    }
1603
1604    /// Selects all the content of the [`TextInput`].
1605    pub fn select_all(&mut self) {
1606        self.cursor.select_range(0, usize::MAX);
1607    }
1608}
1609
1610impl<P: text::Paragraph> operation::Focusable for State<P> {
1611    fn is_focused(&self) -> bool {
1612        State::is_focused(self)
1613    }
1614
1615    fn focus(&mut self) {
1616        State::focus(self);
1617    }
1618
1619    fn unfocus(&mut self) {
1620        State::unfocus(self);
1621    }
1622}
1623
1624impl<P: text::Paragraph> operation::TextInput for State<P> {
1625    fn move_cursor_to_front(&mut self) {
1626        State::move_cursor_to_front(self);
1627    }
1628
1629    fn move_cursor_to_end(&mut self) {
1630        State::move_cursor_to_end(self);
1631    }
1632
1633    fn move_cursor_to(&mut self, position: usize) {
1634        State::move_cursor_to(self, position);
1635    }
1636
1637    fn select_all(&mut self) {
1638        State::select_all(self);
1639    }
1640}
1641
1642fn offset<P: text::Paragraph>(
1643    text_bounds: Rectangle,
1644    value: &Value,
1645    state: &State<P>,
1646) -> f32 {
1647    if state.is_focused() {
1648        let cursor = state.cursor();
1649
1650        let focus_position = match cursor.state(value) {
1651            cursor::State::Index(i) => i,
1652            cursor::State::Selection { end, .. } => end,
1653        };
1654
1655        let (_, offset) = measure_cursor_and_scroll_offset(
1656            state.value.raw(),
1657            text_bounds,
1658            focus_position,
1659        );
1660
1661        offset
1662    } else {
1663        0.0
1664    }
1665}
1666
1667fn measure_cursor_and_scroll_offset(
1668    paragraph: &impl text::Paragraph,
1669    text_bounds: Rectangle,
1670    cursor_index: usize,
1671) -> (f32, f32) {
1672    let grapheme_position = paragraph
1673        .grapheme_position(0, cursor_index)
1674        .unwrap_or(Point::ORIGIN);
1675
1676    let offset = ((grapheme_position.x + 5.0) - text_bounds.width).max(0.0);
1677
1678    (grapheme_position.x, offset)
1679}
1680
1681/// Computes the position of the text cursor at the given X coordinate of
1682/// a [`TextInput`].
1683fn find_cursor_position<P: text::Paragraph>(
1684    text_bounds: Rectangle,
1685    value: &Value,
1686    state: &State<P>,
1687    x: f32,
1688) -> Option<usize> {
1689    let offset = offset(text_bounds, value, state);
1690    let value = value.to_string();
1691
1692    let char_offset = state
1693        .value
1694        .raw()
1695        .hit_test(Point::new(x + offset, text_bounds.height / 2.0))
1696        .map(text::Hit::cursor)?;
1697
1698    Some(
1699        unicode_segmentation::UnicodeSegmentation::graphemes(
1700            &value[..char_offset.min(value.len())],
1701            true,
1702        )
1703        .count(),
1704    )
1705}
1706
1707fn replace_paragraph<Renderer>(
1708    renderer: &Renderer,
1709    state: &mut State<Renderer::Paragraph>,
1710    layout: Layout<'_>,
1711    value: &Value,
1712    font: Option<Renderer::Font>,
1713    text_size: Option<Pixels>,
1714    line_height: text::LineHeight,
1715) where
1716    Renderer: text::Renderer,
1717{
1718    let font = font.unwrap_or_else(|| renderer.default_font());
1719    let text_size = text_size.unwrap_or_else(|| renderer.default_size());
1720
1721    let mut children_layout = layout.children();
1722    let text_bounds = children_layout.next().unwrap().bounds();
1723
1724    state.value = paragraph::Plain::new(Text {
1725        font,
1726        line_height,
1727        content: &value.to_string(),
1728        bounds: Size::new(f32::INFINITY, text_bounds.height),
1729        size: text_size,
1730        align_x: text::Alignment::Default,
1731        align_y: alignment::Vertical::Center,
1732        shaping: text::Shaping::Advanced,
1733        wrapping: text::Wrapping::default(),
1734    });
1735}
1736
1737const CURSOR_BLINK_INTERVAL_MILLIS: u128 = 500;
1738
1739/// The possible status of a [`TextInput`].
1740#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1741pub enum Status {
1742    /// The [`TextInput`] can be interacted with.
1743    Active,
1744    /// The [`TextInput`] is being hovered.
1745    Hovered,
1746    /// The [`TextInput`] is focused.
1747    Focused {
1748        /// Whether the [`TextInput`] is hovered, while focused.
1749        is_hovered: bool,
1750    },
1751    /// The [`TextInput`] cannot be interacted with.
1752    Disabled,
1753}
1754
1755/// The appearance of a text input.
1756#[derive(Debug, Clone, Copy, PartialEq)]
1757pub struct Style {
1758    /// The [`Background`] of the text input.
1759    pub background: Background,
1760    /// The [`Border`] of the text input.
1761    pub border: Border,
1762    /// The [`Color`] of the icon of the text input.
1763    pub icon: Color,
1764    /// The [`Color`] of the placeholder of the text input.
1765    pub placeholder: Color,
1766    /// The [`Color`] of the value of the text input.
1767    pub value: Color,
1768    /// The [`Color`] of the selection of the text input.
1769    pub selection: Color,
1770}
1771
1772/// The theme catalog of a [`TextInput`].
1773pub trait Catalog: Sized {
1774    /// The item class of the [`Catalog`].
1775    type Class<'a>;
1776
1777    /// The default class produced by the [`Catalog`].
1778    fn default<'a>() -> Self::Class<'a>;
1779
1780    /// The [`Style`] of a class with the given status.
1781    fn style(&self, class: &Self::Class<'_>, status: Status) -> Style;
1782}
1783
1784/// A styling function for a [`TextInput`].
1785///
1786/// This is just a boxed closure: `Fn(&Theme, Status) -> Style`.
1787pub type StyleFn<'a, Theme> = Box<dyn Fn(&Theme, Status) -> Style + 'a>;
1788
1789impl Catalog for Theme {
1790    type Class<'a> = StyleFn<'a, Self>;
1791
1792    fn default<'a>() -> Self::Class<'a> {
1793        Box::new(default)
1794    }
1795
1796    fn style(&self, class: &Self::Class<'_>, status: Status) -> Style {
1797        class(self, status)
1798    }
1799}
1800
1801/// The default style of a [`TextInput`].
1802pub fn default(theme: &Theme, status: Status) -> Style {
1803    let palette = theme.extended_palette();
1804
1805    let active = Style {
1806        background: Background::Color(palette.background.base.color),
1807        border: Border {
1808            radius: 2.0.into(),
1809            width: 1.0,
1810            color: palette.background.strongest.color,
1811        },
1812        icon: palette.background.weak.text,
1813        placeholder: palette.background.strongest.color,
1814        value: palette.background.base.text,
1815        selection: palette.primary.weak.color,
1816    };
1817
1818    match status {
1819        Status::Active => active,
1820        Status::Hovered => Style {
1821            border: Border {
1822                color: palette.background.base.text,
1823                ..active.border
1824            },
1825            ..active
1826        },
1827        Status::Focused { .. } => Style {
1828            border: Border {
1829                color: palette.primary.strong.color,
1830                ..active.border
1831            },
1832            ..active
1833        },
1834        Status::Disabled => Style {
1835            background: Background::Color(palette.background.weak.color),
1836            value: active.placeholder,
1837            ..active
1838        },
1839    }
1840}
1841
1842fn alignment_offset(
1843    text_bounds_width: f32,
1844    text_min_width: f32,
1845    alignment: alignment::Horizontal,
1846) -> f32 {
1847    if text_min_width > text_bounds_width {
1848        0.0
1849    } else {
1850        match alignment {
1851            alignment::Horizontal::Left => 0.0,
1852            alignment::Horizontal::Center => {
1853                (text_bounds_width - text_min_width) / 2.0
1854            }
1855            alignment::Horizontal::Right => text_bounds_width - text_min_width,
1856        }
1857    }
1858}