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