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        &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        &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        &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                    if let keyboard::Key::Character("v") = key.as_ref() {
1252                        state.is_pasting = None;
1253
1254                        shell.capture_event();
1255                    }
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                    if focus.is_window_focused {
1333                        if matches!(
1334                            state.cursor.state(&self.value),
1335                            cursor::State::Index(_)
1336                        ) {
1337                            focus.now = *now;
1338
1339                            let millis_until_redraw =
1340                                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
1362        let state = state::<Renderer>(tree);
1363        let is_disabled = self.on_input.is_none();
1364
1365        let status = if is_disabled {
1366            Status::Disabled
1367        } else if state.is_focused() {
1368            Status::Focused {
1369                is_hovered: cursor.is_over(layout.bounds()),
1370            }
1371        } else if cursor.is_over(layout.bounds()) {
1372            Status::Hovered
1373        } else {
1374            Status::Active
1375        };
1376
1377        if let Event::Window(window::Event::RedrawRequested(_now)) = event {
1378            self.last_status = Some(status);
1379        } else if self
1380            .last_status
1381            .is_some_and(|last_status| status != last_status)
1382        {
1383            shell.request_redraw();
1384        }
1385    }
1386
1387    fn draw(
1388        &self,
1389        tree: &Tree,
1390        renderer: &mut Renderer,
1391        theme: &Theme,
1392        _style: &renderer::Style,
1393        layout: Layout<'_>,
1394        cursor: mouse::Cursor,
1395        viewport: &Rectangle,
1396    ) {
1397        self.draw(tree, renderer, theme, layout, cursor, None, viewport);
1398    }
1399
1400    fn mouse_interaction(
1401        &self,
1402        _state: &Tree,
1403        layout: Layout<'_>,
1404        cursor: mouse::Cursor,
1405        _viewport: &Rectangle,
1406        _renderer: &Renderer,
1407    ) -> mouse::Interaction {
1408        if cursor.is_over(layout.bounds()) {
1409            if self.on_input.is_none() {
1410                mouse::Interaction::Idle
1411            } else {
1412                mouse::Interaction::Text
1413            }
1414        } else {
1415            mouse::Interaction::default()
1416        }
1417    }
1418}
1419
1420impl<'a, Message, Theme, Renderer> From<TextInput<'a, Message, Theme, Renderer>>
1421    for Element<'a, Message, Theme, Renderer>
1422where
1423    Message: Clone + 'a,
1424    Theme: Catalog + 'a,
1425    Renderer: text::Renderer + 'a,
1426{
1427    fn from(
1428        text_input: TextInput<'a, Message, Theme, Renderer>,
1429    ) -> Element<'a, Message, Theme, Renderer> {
1430        Element::new(text_input)
1431    }
1432}
1433
1434/// The content of the [`Icon`].
1435#[derive(Debug, Clone)]
1436pub struct Icon<Font> {
1437    /// The font that will be used to display the `code_point`.
1438    pub font: Font,
1439    /// The unicode code point that will be used as the icon.
1440    pub code_point: char,
1441    /// The font size of the content.
1442    pub size: Option<Pixels>,
1443    /// The spacing between the [`Icon`] and the text in a [`TextInput`].
1444    pub spacing: f32,
1445    /// The side of a [`TextInput`] where to display the [`Icon`].
1446    pub side: Side,
1447}
1448
1449/// The side of a [`TextInput`].
1450#[derive(Debug, Clone)]
1451pub enum Side {
1452    /// The left side of a [`TextInput`].
1453    Left,
1454    /// The right side of a [`TextInput`].
1455    Right,
1456}
1457
1458/// The identifier of a [`TextInput`].
1459#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1460pub struct Id(widget::Id);
1461
1462impl Id {
1463    /// Creates a custom [`Id`].
1464    pub fn new(id: impl Into<std::borrow::Cow<'static, str>>) -> Self {
1465        Self(widget::Id::new(id))
1466    }
1467
1468    /// Creates a unique [`Id`].
1469    ///
1470    /// This function produces a different [`Id`] every time it is called.
1471    pub fn unique() -> Self {
1472        Self(widget::Id::unique())
1473    }
1474}
1475
1476impl From<Id> for widget::Id {
1477    fn from(id: Id) -> Self {
1478        id.0
1479    }
1480}
1481
1482impl From<&'static str> for Id {
1483    fn from(id: &'static str) -> Self {
1484        Self::new(id)
1485    }
1486}
1487
1488impl From<String> for Id {
1489    fn from(id: String) -> Self {
1490        Self::new(id)
1491    }
1492}
1493
1494/// Produces a [`Task`] that returns whether the [`TextInput`] with the given [`Id`] is focused or not.
1495pub fn is_focused(id: impl Into<Id>) -> Task<bool> {
1496    task::widget(operation::focusable::is_focused(id.into().into()))
1497}
1498
1499/// Produces a [`Task`] that focuses the [`TextInput`] with the given [`Id`].
1500pub fn focus<T>(id: impl Into<Id>) -> Task<T> {
1501    task::effect(Action::widget(operation::focusable::focus(id.into().0)))
1502}
1503
1504/// Produces a [`Task`] that moves the cursor of the [`TextInput`] with the given [`Id`] to the
1505/// end.
1506pub fn move_cursor_to_end<T>(id: impl Into<Id>) -> Task<T> {
1507    task::effect(Action::widget(operation::text_input::move_cursor_to_end(
1508        id.into().0,
1509    )))
1510}
1511
1512/// Produces a [`Task`] that moves the cursor of the [`TextInput`] with the given [`Id`] to the
1513/// front.
1514pub fn move_cursor_to_front<T>(id: impl Into<Id>) -> Task<T> {
1515    task::effect(Action::widget(operation::text_input::move_cursor_to_front(
1516        id.into().0,
1517    )))
1518}
1519
1520/// Produces a [`Task`] that moves the cursor of the [`TextInput`] with the given [`Id`] to the
1521/// provided position.
1522pub fn move_cursor_to<T>(id: impl Into<Id>, position: usize) -> Task<T> {
1523    task::effect(Action::widget(operation::text_input::move_cursor_to(
1524        id.into().0,
1525        position,
1526    )))
1527}
1528
1529/// Produces a [`Task`] that selects all the content of the [`TextInput`] with the given [`Id`].
1530pub fn select_all<T>(id: impl Into<Id>) -> Task<T> {
1531    task::effect(Action::widget(operation::text_input::select_all(
1532        id.into().0,
1533    )))
1534}
1535
1536/// The state of a [`TextInput`].
1537#[derive(Debug, Default, Clone)]
1538pub struct State<P: text::Paragraph> {
1539    value: paragraph::Plain<P>,
1540    placeholder: paragraph::Plain<P>,
1541    icon: paragraph::Plain<P>,
1542    is_focused: Option<Focus>,
1543    is_dragging: bool,
1544    is_pasting: Option<Value>,
1545    preedit: Option<input_method::Preedit>,
1546    last_click: Option<mouse::Click>,
1547    cursor: Cursor,
1548    keyboard_modifiers: keyboard::Modifiers,
1549    // TODO: Add stateful horizontal scrolling offset
1550}
1551
1552fn state<Renderer: text::Renderer>(
1553    tree: &mut Tree,
1554) -> &mut State<Renderer::Paragraph> {
1555    tree.state.downcast_mut::<State<Renderer::Paragraph>>()
1556}
1557
1558#[derive(Debug, Clone)]
1559struct Focus {
1560    updated_at: Instant,
1561    now: Instant,
1562    is_window_focused: bool,
1563}
1564
1565impl<P: text::Paragraph> State<P> {
1566    /// Creates a new [`State`], representing an unfocused [`TextInput`].
1567    pub fn new() -> Self {
1568        Self::default()
1569    }
1570
1571    /// Returns whether the [`TextInput`] is currently focused or not.
1572    pub fn is_focused(&self) -> bool {
1573        self.is_focused.is_some()
1574    }
1575
1576    /// Returns the [`Cursor`] of the [`TextInput`].
1577    pub fn cursor(&self) -> Cursor {
1578        self.cursor
1579    }
1580
1581    /// Focuses the [`TextInput`].
1582    pub fn focus(&mut self) {
1583        let now = Instant::now();
1584
1585        self.is_focused = Some(Focus {
1586            updated_at: now,
1587            now,
1588            is_window_focused: true,
1589        });
1590
1591        self.move_cursor_to_end();
1592    }
1593
1594    /// Unfocuses the [`TextInput`].
1595    pub fn unfocus(&mut self) {
1596        self.is_focused = None;
1597    }
1598
1599    /// Moves the [`Cursor`] of the [`TextInput`] to the front of the input text.
1600    pub fn move_cursor_to_front(&mut self) {
1601        self.cursor.move_to(0);
1602    }
1603
1604    /// Moves the [`Cursor`] of the [`TextInput`] to the end of the input text.
1605    pub fn move_cursor_to_end(&mut self) {
1606        self.cursor.move_to(usize::MAX);
1607    }
1608
1609    /// Moves the [`Cursor`] of the [`TextInput`] to an arbitrary location.
1610    pub fn move_cursor_to(&mut self, position: usize) {
1611        self.cursor.move_to(position);
1612    }
1613
1614    /// Selects all the content of the [`TextInput`].
1615    pub fn select_all(&mut self) {
1616        self.cursor.select_range(0, usize::MAX);
1617    }
1618}
1619
1620impl<P: text::Paragraph> operation::Focusable for State<P> {
1621    fn is_focused(&self) -> bool {
1622        State::is_focused(self)
1623    }
1624
1625    fn focus(&mut self) {
1626        State::focus(self);
1627    }
1628
1629    fn unfocus(&mut self) {
1630        State::unfocus(self);
1631    }
1632}
1633
1634impl<P: text::Paragraph> operation::TextInput for State<P> {
1635    fn move_cursor_to_front(&mut self) {
1636        State::move_cursor_to_front(self);
1637    }
1638
1639    fn move_cursor_to_end(&mut self) {
1640        State::move_cursor_to_end(self);
1641    }
1642
1643    fn move_cursor_to(&mut self, position: usize) {
1644        State::move_cursor_to(self, position);
1645    }
1646
1647    fn select_all(&mut self) {
1648        State::select_all(self);
1649    }
1650}
1651
1652fn offset<P: text::Paragraph>(
1653    text_bounds: Rectangle,
1654    value: &Value,
1655    state: &State<P>,
1656) -> f32 {
1657    if state.is_focused() {
1658        let cursor = state.cursor();
1659
1660        let focus_position = match cursor.state(value) {
1661            cursor::State::Index(i) => i,
1662            cursor::State::Selection { end, .. } => end,
1663        };
1664
1665        let (_, offset) = measure_cursor_and_scroll_offset(
1666            state.value.raw(),
1667            text_bounds,
1668            focus_position,
1669        );
1670
1671        offset
1672    } else {
1673        0.0
1674    }
1675}
1676
1677fn measure_cursor_and_scroll_offset(
1678    paragraph: &impl text::Paragraph,
1679    text_bounds: Rectangle,
1680    cursor_index: usize,
1681) -> (f32, f32) {
1682    let grapheme_position = paragraph
1683        .grapheme_position(0, cursor_index)
1684        .unwrap_or(Point::ORIGIN);
1685
1686    let offset = ((grapheme_position.x + 5.0) - text_bounds.width).max(0.0);
1687
1688    (grapheme_position.x, offset)
1689}
1690
1691/// Computes the position of the text cursor at the given X coordinate of
1692/// a [`TextInput`].
1693fn find_cursor_position<P: text::Paragraph>(
1694    text_bounds: Rectangle,
1695    value: &Value,
1696    state: &State<P>,
1697    x: f32,
1698) -> Option<usize> {
1699    let offset = offset(text_bounds, value, state);
1700    let value = value.to_string();
1701
1702    let char_offset = state
1703        .value
1704        .raw()
1705        .hit_test(Point::new(x + offset, text_bounds.height / 2.0))
1706        .map(text::Hit::cursor)?;
1707
1708    Some(
1709        unicode_segmentation::UnicodeSegmentation::graphemes(
1710            &value[..char_offset.min(value.len())],
1711            true,
1712        )
1713        .count(),
1714    )
1715}
1716
1717fn replace_paragraph<Renderer>(
1718    renderer: &Renderer,
1719    state: &mut State<Renderer::Paragraph>,
1720    layout: Layout<'_>,
1721    value: &Value,
1722    font: Option<Renderer::Font>,
1723    text_size: Option<Pixels>,
1724    line_height: text::LineHeight,
1725) where
1726    Renderer: text::Renderer,
1727{
1728    let font = font.unwrap_or_else(|| renderer.default_font());
1729    let text_size = text_size.unwrap_or_else(|| renderer.default_size());
1730
1731    let mut children_layout = layout.children();
1732    let text_bounds = children_layout.next().unwrap().bounds();
1733
1734    state.value = paragraph::Plain::new(Text {
1735        font,
1736        line_height,
1737        content: value.to_string(),
1738        bounds: Size::new(f32::INFINITY, text_bounds.height),
1739        size: text_size,
1740        align_x: text::Alignment::Default,
1741        align_y: alignment::Vertical::Center,
1742        shaping: text::Shaping::Advanced,
1743        wrapping: text::Wrapping::default(),
1744    });
1745}
1746
1747const CURSOR_BLINK_INTERVAL_MILLIS: u128 = 500;
1748
1749/// The possible status of a [`TextInput`].
1750#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1751pub enum Status {
1752    /// The [`TextInput`] can be interacted with.
1753    Active,
1754    /// The [`TextInput`] is being hovered.
1755    Hovered,
1756    /// The [`TextInput`] is focused.
1757    Focused {
1758        /// Whether the [`TextInput`] is hovered, while focused.
1759        is_hovered: bool,
1760    },
1761    /// The [`TextInput`] cannot be interacted with.
1762    Disabled,
1763}
1764
1765/// The appearance of a text input.
1766#[derive(Debug, Clone, Copy, PartialEq)]
1767pub struct Style {
1768    /// The [`Background`] of the text input.
1769    pub background: Background,
1770    /// The [`Border`] of the text input.
1771    pub border: Border,
1772    /// The [`Color`] of the icon of the text input.
1773    pub icon: Color,
1774    /// The [`Color`] of the placeholder of the text input.
1775    pub placeholder: Color,
1776    /// The [`Color`] of the value of the text input.
1777    pub value: Color,
1778    /// The [`Color`] of the selection of the text input.
1779    pub selection: Color,
1780}
1781
1782/// The theme catalog of a [`TextInput`].
1783pub trait Catalog: Sized {
1784    /// The item class of the [`Catalog`].
1785    type Class<'a>;
1786
1787    /// The default class produced by the [`Catalog`].
1788    fn default<'a>() -> Self::Class<'a>;
1789
1790    /// The [`Style`] of a class with the given status.
1791    fn style(&self, class: &Self::Class<'_>, status: Status) -> Style;
1792}
1793
1794/// A styling function for a [`TextInput`].
1795///
1796/// This is just a boxed closure: `Fn(&Theme, Status) -> Style`.
1797pub type StyleFn<'a, Theme> = Box<dyn Fn(&Theme, Status) -> Style + 'a>;
1798
1799impl Catalog for Theme {
1800    type Class<'a> = StyleFn<'a, Self>;
1801
1802    fn default<'a>() -> Self::Class<'a> {
1803        Box::new(default)
1804    }
1805
1806    fn style(&self, class: &Self::Class<'_>, status: Status) -> Style {
1807        class(self, status)
1808    }
1809}
1810
1811/// The default style of a [`TextInput`].
1812pub fn default(theme: &Theme, status: Status) -> Style {
1813    let palette = theme.extended_palette();
1814
1815    let active = Style {
1816        background: Background::Color(palette.background.base.color),
1817        border: Border {
1818            radius: 2.0.into(),
1819            width: 1.0,
1820            color: palette.background.strongest.color,
1821        },
1822        icon: palette.background.weak.text,
1823        placeholder: palette.background.strongest.color,
1824        value: palette.background.base.text,
1825        selection: palette.primary.weak.color,
1826    };
1827
1828    match status {
1829        Status::Active => active,
1830        Status::Hovered => Style {
1831            border: Border {
1832                color: palette.background.base.text,
1833                ..active.border
1834            },
1835            ..active
1836        },
1837        Status::Focused { .. } => Style {
1838            border: Border {
1839                color: palette.primary.strong.color,
1840                ..active.border
1841            },
1842            ..active
1843        },
1844        Status::Disabled => Style {
1845            background: Background::Color(palette.background.weak.color),
1846            value: active.placeholder,
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}