1use std::borrow::Cow;
9use std::cell::{Cell, LazyCell};
10
11use crate::ext::ColorExt;
12use crate::theme::THEME;
13
14use super::cursor;
15pub use super::cursor::Cursor;
16use super::editor::Editor;
17use super::style::StyleSheet;
18pub use super::value::Value;
19
20use apply::Apply;
21use iced::Limits;
22use iced::clipboard::dnd::{DndAction, DndEvent, OfferEvent, SourceEvent};
23use iced::clipboard::mime::AsMimeTypes;
24use iced_core::event::{self, Event};
25use iced_core::mouse::{self, click};
26use iced_core::overlay::Group;
27use iced_core::renderer::{self, Renderer as CoreRenderer};
28use iced_core::text::{self, Paragraph, Renderer, Text};
29use iced_core::time::{Duration, Instant};
30use iced_core::touch;
31use iced_core::widget::Id;
32use iced_core::widget::operation::{self, Operation};
33use iced_core::widget::tree::{self, Tree};
34use iced_core::window;
35use iced_core::{Background, alignment};
36use iced_core::{Border, Shadow, keyboard};
37use iced_core::{
38    Clipboard, Color, Element, Layout, Length, Padding, Pixels, Point, Rectangle, Shell, Size,
39    Vector, Widget,
40};
41use iced_core::{layout, overlay};
42use iced_runtime::{Action, Task, task};
43
44thread_local! {
45    static LAST_FOCUS_UPDATE: LazyCell<Cell<Instant>> = LazyCell::new(|| Cell::new(Instant::now()));
47}
48
49pub fn text_input<'a, Message>(
53    placeholder: impl Into<Cow<'a, str>>,
54    value: impl Into<Cow<'a, str>>,
55) -> TextInput<'a, Message>
56where
57    Message: Clone + 'static,
58{
59    TextInput::new(placeholder, value)
60}
61
62pub fn editable_input<'a, Message: Clone + 'static>(
64    placeholder: impl Into<Cow<'a, str>>,
65    text: impl Into<Cow<'a, str>>,
66    editing: bool,
67    on_toggle_edit: impl Fn(bool) -> Message + 'a,
68) -> TextInput<'a, Message> {
69    let icon = crate::widget::icon::from_name(if editing {
70        "edit-clear-symbolic"
71    } else {
72        "edit-symbolic"
73    });
74
75    TextInput::new(placeholder, text)
76        .style(crate::theme::TextInput::EditableText)
77        .editable()
78        .editing(editing)
79        .on_toggle_edit(on_toggle_edit)
80        .trailing_icon(icon.size(16).into())
81}
82
83pub fn search_input<'a, Message>(
87    placeholder: impl Into<Cow<'a, str>>,
88    value: impl Into<Cow<'a, str>>,
89) -> TextInput<'a, Message>
90where
91    Message: Clone + 'static,
92{
93    let spacing = THEME.lock().unwrap().cosmic().space_xxs();
94
95    TextInput::new(placeholder, value)
96        .padding([0, spacing])
97        .style(crate::theme::TextInput::Search)
98        .leading_icon(
99            crate::widget::icon::from_name("system-search-symbolic")
100                .size(16)
101                .apply(crate::widget::container)
102                .padding(8)
103                .into(),
104        )
105}
106pub fn secure_input<'a, Message>(
110    placeholder: impl Into<Cow<'a, str>>,
111    value: impl Into<Cow<'a, str>>,
112    on_visible_toggle: Option<Message>,
113    hidden: bool,
114) -> TextInput<'a, Message>
115where
116    Message: Clone + 'static,
117{
118    let spacing = THEME.lock().unwrap().cosmic().space_xxs();
119    let mut input = TextInput::new(placeholder, value)
120        .padding([0, spacing])
121        .style(crate::theme::TextInput::Default)
122        .leading_icon(
123            crate::widget::icon::from_name("system-lock-screen-symbolic")
124                .size(16)
125                .apply(crate::widget::container)
126                .padding(8)
127                .into(),
128        );
129    if hidden {
130        input = input.password();
131    }
132    if let Some(msg) = on_visible_toggle {
133        input.trailing_icon(
134            crate::widget::icon::from_name(if hidden {
135                "document-properties-symbolic"
136            } else {
137                "image-red-eye-symbolic"
138            })
139            .size(16)
140            .apply(crate::widget::button::custom)
141            .class(crate::theme::Button::Icon)
142            .on_press(msg)
143            .padding(8)
144            .into(),
145        )
146    } else {
147        input
148    }
149}
150
151pub fn inline_input<'a, Message>(
155    placeholder: impl Into<Cow<'a, str>>,
156    value: impl Into<Cow<'a, str>>,
157) -> TextInput<'a, Message>
158where
159    Message: Clone + 'static,
160{
161    let spacing = THEME.lock().unwrap().cosmic().space_xxs();
162
163    TextInput::new(placeholder, value)
164        .style(crate::theme::TextInput::Inline)
165        .padding(spacing)
166}
167
168pub(crate) const SUPPORTED_TEXT_MIME_TYPES: &[&str; 6] = &[
169    "text/plain;charset=utf-8",
170    "text/plain;charset=UTF-8",
171    "UTF8_STRING",
172    "STRING",
173    "text/plain",
174    "TEXT",
175];
176
177#[allow(missing_debug_implementations)]
179#[must_use]
180pub struct TextInput<'a, Message> {
181    id: Id,
182    placeholder: Cow<'a, str>,
183    value: Value,
184    is_secure: bool,
185    is_editable_variant: bool,
186    is_read_only: bool,
187    select_on_focus: bool,
188    font: Option<<crate::Renderer as iced_core::text::Renderer>::Font>,
189    width: Length,
190    padding: Padding,
191    size: Option<f32>,
192    helper_size: f32,
193    label: Option<Cow<'a, str>>,
194    helper_text: Option<Cow<'a, str>>,
195    error: Option<Cow<'a, str>>,
196    on_focus: Option<Message>,
197    on_unfocus: Option<Message>,
198    on_input: Option<Box<dyn Fn(String) -> Message + 'a>>,
199    on_paste: Option<Box<dyn Fn(String) -> Message + 'a>>,
200    on_tab: Option<Message>,
201    on_submit: Option<Box<dyn Fn(String) -> Message + 'a>>,
202    on_toggle_edit: Option<Box<dyn Fn(bool) -> Message + 'a>>,
203    leading_icon: Option<Element<'a, Message, crate::Theme, crate::Renderer>>,
204    trailing_icon: Option<Element<'a, Message, crate::Theme, crate::Renderer>>,
205    style: <crate::Theme as StyleSheet>::Style,
206    on_create_dnd_source: Option<Box<dyn Fn(State) -> Message + 'a>>,
207    surface_ids: Option<(window::Id, window::Id)>,
208    dnd_icon: bool,
209    line_height: text::LineHeight,
210    helper_line_height: text::LineHeight,
211    always_active: bool,
212    manage_value: bool,
214    drag_threshold: f32,
215}
216
217impl<'a, Message> TextInput<'a, Message>
218where
219    Message: Clone + 'static,
220{
221    pub fn new(placeholder: impl Into<Cow<'a, str>>, value: impl Into<Cow<'a, str>>) -> Self {
227        let spacing = THEME.lock().unwrap().cosmic().space_xxs();
228
229        let v: Cow<'a, str> = value.into();
230        TextInput {
231            id: Id::unique(),
232            placeholder: placeholder.into(),
233            value: Value::new(v.as_ref()),
234            is_secure: false,
235            is_editable_variant: false,
236            is_read_only: false,
237            select_on_focus: false,
238            font: None,
239            width: Length::Fill,
240            padding: spacing.into(),
241            size: None,
242            helper_size: 10.0,
243            helper_line_height: text::LineHeight::Absolute(14.0.into()),
244            on_focus: None,
245            on_unfocus: None,
246            on_input: None,
247            on_paste: None,
248            on_submit: None,
249            on_tab: None,
250            on_toggle_edit: None,
251            leading_icon: None,
252            trailing_icon: None,
253            error: None,
254            style: crate::theme::TextInput::default(),
255            on_create_dnd_source: None,
256            surface_ids: None,
257            dnd_icon: false,
258            line_height: text::LineHeight::default(),
259            label: None,
260            helper_text: None,
261            always_active: false,
262            manage_value: false,
263            drag_threshold: 20.0,
264        }
265    }
266
267    #[inline]
268    fn dnd_id(&self) -> u128 {
269        match &self.id.0 {
270            iced_core::id::Internal::Custom(id, _) | iced_core::id::Internal::Unique(id) => {
271                *id as u128
272            }
273            _ => unreachable!(),
274        }
275    }
276
277    #[inline]
280    pub const fn always_active(mut self) -> Self {
281        self.always_active = true;
282        self
283    }
284
285    pub fn label(mut self, label: impl Into<Cow<'a, str>>) -> Self {
287        self.label = Some(label.into());
288        self
289    }
290
291    pub fn helper_text(mut self, helper_text: impl Into<Cow<'a, str>>) -> Self {
293        self.helper_text = Some(helper_text.into());
294        self
295    }
296
297    #[inline]
299    pub fn id(mut self, id: Id) -> Self {
300        self.id = id;
301        self
302    }
303
304    pub fn error(mut self, error: impl Into<Cow<'a, str>>) -> Self {
306        self.error = Some(error.into());
307        self
308    }
309
310    pub fn line_height(mut self, line_height: impl Into<text::LineHeight>) -> Self {
312        self.line_height = line_height.into();
313        self
314    }
315
316    #[inline]
318    pub const fn password(mut self) -> Self {
319        self.is_secure = true;
320        self
321    }
322
323    #[inline]
325    pub(crate) const fn editable(mut self) -> Self {
326        self.is_editable_variant = true;
327        self
328    }
329
330    #[inline]
331    pub const fn editing(mut self, enable: bool) -> Self {
332        self.is_read_only = !enable;
333        self
334    }
335
336    #[inline]
338    pub const fn select_on_focus(mut self, select_on_focus: bool) -> Self {
339        self.select_on_focus = select_on_focus;
340        self
341    }
342
343    #[inline]
347    pub fn on_focus(mut self, on_focus: Message) -> Self {
348        self.on_focus = Some(on_focus);
349        self
350    }
351
352    #[inline]
356    pub fn on_unfocus(mut self, on_unfocus: Message) -> Self {
357        self.on_unfocus = Some(on_unfocus);
358        self
359    }
360
361    pub fn on_input(mut self, callback: impl Fn(String) -> Message + 'a) -> Self {
366        self.on_input = Some(Box::new(callback));
367        self
368    }
369
370    pub fn on_submit(mut self, callback: impl Fn(String) -> Message + 'a) -> Self {
372        self.on_submit = Some(Box::new(callback));
373        self
374    }
375
376    pub fn on_submit_maybe(self, callback: Option<impl Fn(String) -> Message + 'a>) -> Self {
378        if let Some(callback) = callback {
379            self.on_submit(callback)
380        } else {
381            self
382        }
383    }
384
385    #[inline]
389    pub fn on_tab(mut self, on_tab: Message) -> Self {
390        self.on_tab = Some(on_tab);
391        self
392    }
393
394    pub fn on_toggle_edit(mut self, callback: impl Fn(bool) -> Message + 'a) -> Self {
396        self.on_toggle_edit = Some(Box::new(callback));
397        self
398    }
399
400    pub fn on_paste(mut self, on_paste: impl Fn(String) -> Message + 'a) -> Self {
403        self.on_paste = Some(Box::new(on_paste));
404        self
405    }
406
407    #[inline]
411    pub const fn font(
412        mut self,
413        font: <crate::Renderer as iced_core::text::Renderer>::Font,
414    ) -> Self {
415        self.font = Some(font);
416        self
417    }
418
419    #[inline]
421    pub fn leading_icon(
422        mut self,
423        icon: Element<'a, Message, crate::Theme, crate::Renderer>,
424    ) -> Self {
425        self.leading_icon = Some(icon);
426        self
427    }
428
429    #[inline]
431    pub fn trailing_icon(
432        mut self,
433        icon: Element<'a, Message, crate::Theme, crate::Renderer>,
434    ) -> Self {
435        self.trailing_icon = Some(icon);
436        self
437    }
438
439    pub fn width(mut self, width: impl Into<Length>) -> Self {
441        self.width = width.into();
442        self
443    }
444
445    pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
447        self.padding = padding.into();
448        self
449    }
450
451    pub fn size(mut self, size: impl Into<Pixels>) -> Self {
453        self.size = Some(size.into().0);
454        self
455    }
456
457    pub fn style(mut self, style: impl Into<<crate::Theme as StyleSheet>::Style>) -> Self {
459        self.style = style.into();
460        self
461    }
462
463    #[inline]
465    pub const fn manage_value(mut self, manage_value: bool) -> Self {
466        self.manage_value = manage_value;
467        self
468    }
469
470    #[allow(clippy::too_many_arguments)]
475    #[inline]
476    pub fn draw(
477        &self,
478        tree: &Tree,
479        renderer: &mut crate::Renderer,
480        theme: &crate::Theme,
481        layout: Layout<'_>,
482        cursor_position: mouse::Cursor,
483        value: Option<&Value>,
484        style: &renderer::Style,
485    ) {
486        let text_layout = self.text_layout(layout);
487        draw(
488            renderer,
489            theme,
490            layout,
491            text_layout,
492            cursor_position,
493            tree,
494            value.unwrap_or(&self.value),
495            &self.placeholder,
496            self.size,
497            self.font,
498            self.on_input.is_none(),
499            self.is_secure,
500            self.leading_icon.as_ref(),
501            self.trailing_icon.as_ref(),
502            &self.style,
503            self.dnd_icon,
504            self.line_height,
505            self.error.as_deref(),
506            self.label.as_deref(),
507            self.helper_text.as_deref(),
508            self.helper_size,
509            self.helper_line_height,
510            &layout.bounds(),
511            style,
512        );
513    }
514
515    #[cfg(feature = "wayland")]
517    pub fn on_start_dnd(mut self, on_start_dnd: impl Fn(State) -> Message + 'a) -> Self {
518        self.on_create_dnd_source = Some(Box::new(on_start_dnd));
519        self
520    }
521
522    #[inline]
526    pub const fn surface_ids(mut self, window_id: (window::Id, window::Id)) -> Self {
527        self.surface_ids = Some(window_id);
528        self
529    }
530
531    #[inline]
533    pub const fn dnd_icon(mut self, dnd_icon: bool) -> Self {
534        self.dnd_icon = dnd_icon;
535        self
536    }
537
538    pub fn on_clear(self, on_clear: Message) -> Self {
539        self.trailing_icon(
540            crate::widget::icon::from_name("edit-clear-symbolic")
541                .size(16)
542                .apply(crate::widget::button::custom)
543                .class(crate::theme::Button::Icon)
544                .on_press(on_clear)
545                .padding(8)
546                .into(),
547        )
548    }
549
550    fn text_layout<'b>(&'a self, layout: Layout<'b>) -> Layout<'b> {
552        if self.dnd_icon {
553            layout
554        } else if self.label.is_some() {
555            let mut nodes = layout.children();
556            nodes.next();
557            nodes.next().unwrap()
558        } else {
559            layout.children().next().unwrap()
560        }
561    }
562
563    pub fn drag_threshold(mut self, drag_threshold: f32) -> Self {
565        self.drag_threshold = drag_threshold;
566        self
567    }
568}
569
570impl<Message> Widget<Message, crate::Theme, crate::Renderer> for TextInput<'_, Message>
571where
572    Message: Clone + 'static,
573{
574    #[inline]
575    fn tag(&self) -> tree::Tag {
576        tree::Tag::of::<State>()
577    }
578
579    #[inline]
580    fn state(&self) -> tree::State {
581        tree::State::new(State::new(
582            self.is_secure,
583            self.is_read_only,
584            self.always_active,
585            self.select_on_focus,
586        ))
587    }
588
589    fn diff(&mut self, tree: &mut Tree) {
590        let state = tree.state.downcast_mut::<State>();
591
592        if !self.manage_value || !self.value.is_empty() && state.tracked_value != self.value {
593            state.tracked_value = self.value.clone();
594        } else if self.value.is_empty() {
595            self.value = state.tracked_value.clone();
596            }
598        if self.on_input.is_none() && !self.manage_value {
600            state.last_click = None;
601            state.is_focused = state.is_focused.map(|mut f| {
602                f.focused = false;
603                f
604            });
605            state.is_pasting = None;
606            state.dragging_state = None;
607        }
608        let old_value = state
609            .value
610            .raw()
611            .buffer()
612            .lines
613            .iter()
614            .map(|l| l.text())
615            .collect::<String>();
616        if state.is_secure != self.is_secure
617            || old_value != self.value.to_string()
618            || state
619                .label
620                .raw()
621                .buffer()
622                .lines
623                .iter()
624                .map(|l| l.text())
625                .collect::<String>()
626                != self.label.as_deref().unwrap_or_default()
627            || state
628                .helper_text
629                .raw()
630                .buffer()
631                .lines
632                .iter()
633                .map(|l| l.text())
634                .collect::<String>()
635                != self.helper_text.as_deref().unwrap_or_default()
636        {
637            state.is_secure = self.is_secure;
638            state.dirty = true;
639        }
640
641        if self.always_active && !state.is_focused() {
642            let now = Instant::now();
643            LAST_FOCUS_UPDATE.with(|x| x.set(now));
644            state.is_focused = Some(Focus {
645                updated_at: now,
646                now,
647                focused: true,
648                needs_update: false,
649            });
650        }
651
652        let old_value = Value::new(&old_value);
654        if state.is_focused() {
655            if let cursor::State::Index(index) = state.cursor.state(&old_value) {
656                if index == old_value.len() {
657                    state.cursor.move_to(self.value.len());
658                }
659            }
660        }
661
662        if let Some(f) = state.is_focused.as_ref().filter(|f| f.focused) {
663            if f.updated_at != LAST_FOCUS_UPDATE.with(|f| f.get()) {
664                state.unfocus();
665                state.emit_unfocus = true;
666            }
667        }
668
669        self.is_read_only = state.is_read_only;
670
671        if !self.manage_value && self.on_input.is_none() {
673            state.is_pasting = None;
674        }
675
676        let mut children: Vec<_> = self
677            .leading_icon
678            .iter_mut()
679            .chain(self.trailing_icon.iter_mut())
680            .map(iced_core::Element::as_widget_mut)
681            .collect();
682        tree.diff_children(children.as_mut_slice());
683    }
684
685    fn children(&self) -> Vec<Tree> {
686        self.leading_icon
687            .iter()
688            .chain(self.trailing_icon.iter())
689            .map(|icon| Tree::new(icon))
690            .collect()
691    }
692
693    #[inline]
694    fn size(&self) -> Size<Length> {
695        Size {
696            width: self.width,
697            height: Length::Shrink,
698        }
699    }
700
701    fn layout(
702        &self,
703        tree: &mut Tree,
704        renderer: &crate::Renderer,
705        limits: &layout::Limits,
706    ) -> layout::Node {
707        let font = self.font.unwrap_or_else(|| renderer.default_font());
708        if self.dnd_icon {
709            let state = tree.state.downcast_mut::<State>();
710            let limits = limits.width(Length::Shrink).height(Length::Shrink);
711
712            let size = self.size.unwrap_or_else(|| renderer.default_size().0);
713
714            let bounds = limits.resolve(Length::Shrink, Length::Fill, Size::INFINITY);
715            let value_paragraph = &mut state.value;
716            let v = self.value.to_string();
717            value_paragraph.update(Text {
718                content: if self.value.is_empty() {
719                    self.placeholder.as_ref()
720                } else {
721                    &v
722                },
723                font,
724                bounds,
725                size: iced::Pixels(size),
726                horizontal_alignment: alignment::Horizontal::Left,
727                vertical_alignment: alignment::Vertical::Center,
728                line_height: text::LineHeight::default(),
729                shaping: text::Shaping::Advanced,
730                wrapping: text::Wrapping::None,
731            });
732
733            let Size { width, height } =
734                limits.resolve(Length::Shrink, Length::Shrink, value_paragraph.min_bounds());
735
736            let size = limits.resolve(width, height, Size::new(width, height));
737            layout::Node::with_children(size, vec![layout::Node::new(size)])
738        } else {
739            let res = layout(
740                renderer,
741                limits,
742                self.width,
743                self.padding,
744                self.size,
745                self.leading_icon.as_ref(),
746                self.trailing_icon.as_ref(),
747                self.line_height,
748                self.label.as_deref(),
749                self.helper_text.as_deref(),
750                self.helper_size,
751                self.helper_line_height,
752                font,
753                tree,
754            );
755
756            let size = self.size.unwrap_or_else(|| renderer.default_size().0);
758            let line_height = self.line_height;
759            let state = tree.state.downcast_mut::<State>();
760            if state.dirty {
761                state.dirty = false;
762                let value = if self.is_secure {
763                    &self.value.secure()
764                } else {
765                    &self.value
766                };
767                replace_paragraph(
768                    state,
769                    Layout::new(&res),
770                    value,
771                    font,
772                    iced::Pixels(size),
773                    line_height,
774                    limits,
775                );
776            }
777            res
778        }
779    }
780
781    fn operate(
782        &self,
783        tree: &mut Tree,
784        _layout: Layout<'_>,
785        _renderer: &crate::Renderer,
786        operation: &mut dyn Operation<()>,
787    ) {
788        let state = tree.state.downcast_mut::<State>();
789
790        operation.custom(state, Some(&self.id));
791        operation.focusable(state, Some(&self.id));
792        operation.text_input(state, Some(&self.id));
793    }
794
795    fn overlay<'b>(
796        &'b mut self,
797        tree: &'b mut Tree,
798        layout: Layout<'_>,
799        renderer: &crate::Renderer,
800        translation: Vector,
801    ) -> Option<overlay::Element<'b, Message, crate::Theme, crate::Renderer>> {
802        let mut layout_ = Vec::with_capacity(2);
803        if self.leading_icon.is_some() {
804            let mut children = self.text_layout(layout).children();
805            children.next();
806            layout_.push(children.next().unwrap());
807        }
808        if self.trailing_icon.is_some() {
809            let mut children = self.text_layout(layout).children();
810            children.next();
811            if self.leading_icon.is_some() {
812                children.next();
813            }
814            layout_.push(children.next().unwrap());
815        };
816        let children = self
817            .leading_icon
818            .iter_mut()
819            .chain(self.trailing_icon.iter_mut())
820            .zip(&mut tree.children)
821            .zip(layout_)
822            .filter_map(|((child, state), layout)| {
823                child
824                    .as_widget_mut()
825                    .overlay(state, layout, renderer, translation)
826            })
827            .collect::<Vec<_>>();
828
829        (!children.is_empty()).then(|| Group::with_children(children).overlay())
830    }
831
832    fn on_event(
833        &mut self,
834        tree: &mut Tree,
835        event: Event,
836        layout: Layout<'_>,
837        cursor_position: mouse::Cursor,
838        renderer: &crate::Renderer,
839        clipboard: &mut dyn Clipboard,
840        shell: &mut Shell<'_, Message>,
841        viewport: &Rectangle,
842    ) -> event::Status {
843        let text_layout = self.text_layout(layout);
844        let mut trailing_icon_layout = None;
845        let font = self.font.unwrap_or_else(|| renderer.default_font());
846        let size = self.size.unwrap_or_else(|| renderer.default_size().0);
847        let line_height = self.line_height;
848
849        if self.is_editable_variant {
851            if let Some(ref on_edit) = self.on_toggle_edit {
852                let state = tree.state.downcast_mut::<State>();
853                if !state.is_read_only && state.is_focused.is_some_and(|f| !f.focused) {
854                    state.is_read_only = true;
855                    shell.publish((on_edit)(false));
856                } else if state.is_focused() && state.is_read_only {
857                    state.is_read_only = false;
858                    shell.publish((on_edit)(true));
859                } else if let Some(f) = state.is_focused.as_mut().filter(|f| f.needs_update) {
860                    f.needs_update = false;
862                    state.is_read_only = true;
863                    shell.publish((on_edit)(f.focused));
864                }
865            }
866        }
867
868        if !tree.children.is_empty() {
870            let index = tree.children.len() - 1;
871            if let (Some(trailing_icon), Some(tree)) =
872                (self.trailing_icon.as_mut(), tree.children.get_mut(index))
873            {
874                trailing_icon_layout = Some(text_layout.children().last().unwrap());
875
876                if !self.is_editable_variant {
878                    if let Some(trailing_layout) = trailing_icon_layout {
879                        let res = trailing_icon.as_widget_mut().on_event(
880                            tree,
881                            event.clone(),
882                            trailing_layout,
883                            cursor_position,
884                            renderer,
885                            clipboard,
886                            shell,
887                            viewport,
888                        );
889
890                        if res == event::Status::Captured {
891                            return res;
892                        }
893                    }
894                }
895            }
896        }
897
898        let state = tree.state.downcast_mut::<State>();
899
900        if let Some(on_unfocus) = self.on_unfocus.as_ref() {
901            if state.emit_unfocus {
902                state.emit_unfocus = false;
903                shell.publish(on_unfocus.clone());
904            }
905        }
906
907        let dnd_id = self.dnd_id();
908        let id = Widget::id(self);
909        update(
910            id,
911            event,
912            text_layout.children().next().unwrap(),
913            trailing_icon_layout,
914            cursor_position,
915            clipboard,
916            shell,
917            &mut self.value,
918            size,
919            font,
920            self.is_editable_variant,
921            self.is_secure,
922            self.on_focus.as_ref(),
923            self.on_unfocus.as_ref(),
924            self.on_input.as_deref(),
925            self.on_paste.as_deref(),
926            self.on_submit.as_deref(),
927            self.on_tab.as_ref(),
928            self.on_toggle_edit.as_deref(),
929            || tree.state.downcast_mut::<State>(),
930            self.on_create_dnd_source.as_deref(),
931            dnd_id,
932            line_height,
933            layout,
934            self.manage_value,
935            self.drag_threshold,
936        )
937    }
938
939    #[inline]
940    fn draw(
941        &self,
942        tree: &Tree,
943        renderer: &mut crate::Renderer,
944        theme: &crate::Theme,
945        style: &renderer::Style,
946        layout: Layout<'_>,
947        cursor_position: mouse::Cursor,
948        viewport: &Rectangle,
949    ) {
950        let text_layout = self.text_layout(layout);
951        draw(
952            renderer,
953            theme,
954            layout,
955            text_layout,
956            cursor_position,
957            tree,
958            &self.value,
959            &self.placeholder,
960            self.size,
961            self.font,
962            self.on_input.is_none() && !self.manage_value,
963            self.is_secure,
964            self.leading_icon.as_ref(),
965            self.trailing_icon.as_ref(),
966            &self.style,
967            self.dnd_icon,
968            self.line_height,
969            self.error.as_deref(),
970            self.label.as_deref(),
971            self.helper_text.as_deref(),
972            self.helper_size,
973            self.helper_line_height,
974            viewport,
975            style,
976        );
977    }
978
979    fn mouse_interaction(
980        &self,
981        state: &Tree,
982        layout: Layout<'_>,
983        cursor_position: mouse::Cursor,
984        viewport: &Rectangle,
985        renderer: &crate::Renderer,
986    ) -> mouse::Interaction {
987        let layout = self.text_layout(layout);
988        let mut index = 0;
989        if let (Some(leading_icon), Some(tree)) =
990            (self.leading_icon.as_ref(), state.children.get(index))
991        {
992            let leading_icon_layout = layout.children().nth(1).unwrap();
993
994            if cursor_position.is_over(leading_icon_layout.bounds()) {
995                return leading_icon.as_widget().mouse_interaction(
996                    tree,
997                    layout,
998                    cursor_position,
999                    viewport,
1000                    renderer,
1001                );
1002            }
1003            index += 1;
1004        }
1005
1006        if let (Some(trailing_icon), Some(tree)) =
1007            (self.trailing_icon.as_ref(), state.children.get(index))
1008        {
1009            let mut children = layout.children();
1010            children.next();
1011            if self.leading_icon.is_some() {
1013                children.next();
1014            }
1015            let trailing_icon_layout = children.next().unwrap();
1016
1017            if cursor_position.is_over(trailing_icon_layout.bounds()) {
1018                return trailing_icon.as_widget().mouse_interaction(
1019                    tree,
1020                    layout,
1021                    cursor_position,
1022                    viewport,
1023                    renderer,
1024                );
1025            }
1026        }
1027        let mut children = layout.children();
1028        let layout = children.next().unwrap();
1029        mouse_interaction(
1030            layout,
1031            cursor_position,
1032            self.on_input.is_none() && !self.manage_value,
1033        )
1034    }
1035
1036    #[inline]
1037    fn id(&self) -> Option<Id> {
1038        Some(self.id.clone())
1039    }
1040
1041    #[inline]
1042    fn set_id(&mut self, id: Id) {
1043        self.id = id;
1044    }
1045
1046    fn drag_destinations(
1047        &self,
1048        _state: &Tree,
1049        layout: Layout<'_>,
1050        _renderer: &crate::Renderer,
1051        dnd_rectangles: &mut iced_core::clipboard::DndDestinationRectangles,
1052    ) {
1053        if let Some(input) = layout.children().last() {
1054            let Rectangle {
1055                x,
1056                y,
1057                width,
1058                height,
1059            } = input.bounds();
1060            dnd_rectangles.push(iced::clipboard::dnd::DndDestinationRectangle {
1061                id: self.dnd_id(),
1062                rectangle: iced::clipboard::dnd::Rectangle {
1063                    x: x as f64,
1064                    y: y as f64,
1065                    width: width as f64,
1066                    height: height as f64,
1067                },
1068                mime_types: SUPPORTED_TEXT_MIME_TYPES
1069                    .iter()
1070                    .map(|s| Cow::Borrowed(*s))
1071                    .collect(),
1072                actions: DndAction::Move,
1073                preferred: DndAction::Move,
1074            });
1075        }
1076    }
1077}
1078
1079impl<'a, Message> From<TextInput<'a, Message>>
1080    for Element<'a, Message, crate::Theme, crate::Renderer>
1081where
1082    Message: 'static + Clone,
1083{
1084    fn from(
1085        text_input: TextInput<'a, Message>,
1086    ) -> Element<'a, Message, crate::Theme, crate::Renderer> {
1087        Element::new(text_input)
1088    }
1089}
1090
1091pub fn focus<Message: 'static>(id: Id) -> Task<Message> {
1093    task::effect(Action::widget(operation::focusable::focus(id)))
1094}
1095
1096pub fn move_cursor_to_end<Message: 'static>(id: Id) -> Task<Message> {
1099    task::effect(Action::widget(operation::text_input::move_cursor_to_end(
1100        id,
1101    )))
1102}
1103
1104pub fn move_cursor_to_front<Message: 'static>(id: Id) -> Task<Message> {
1107    task::effect(Action::widget(operation::text_input::move_cursor_to_front(
1108        id,
1109    )))
1110}
1111
1112pub fn move_cursor_to<Message: 'static>(id: Id, position: usize) -> Task<Message> {
1115    task::effect(Action::widget(operation::text_input::move_cursor_to(
1116        id, position,
1117    )))
1118}
1119
1120pub fn select_all<Message: 'static>(id: Id) -> Task<Message> {
1122    task::effect(Action::widget(operation::text_input::select_all(id)))
1123}
1124
1125#[allow(clippy::cast_precision_loss)]
1127#[allow(clippy::too_many_arguments)]
1128#[allow(clippy::too_many_lines)]
1129pub fn layout<Message>(
1130    renderer: &crate::Renderer,
1131    limits: &layout::Limits,
1132    width: Length,
1133    padding: Padding,
1134    size: Option<f32>,
1135    leading_icon: Option<&Element<'_, Message, crate::Theme, crate::Renderer>>,
1136    trailing_icon: Option<&Element<'_, Message, crate::Theme, crate::Renderer>>,
1137    line_height: text::LineHeight,
1138    label: Option<&str>,
1139    helper_text: Option<&str>,
1140    helper_text_size: f32,
1141    helper_text_line_height: text::LineHeight,
1142    font: iced_core::Font,
1143    tree: &mut Tree,
1144) -> layout::Node {
1145    let limits = limits.width(width);
1146    let spacing = THEME.lock().unwrap().cosmic().space_xxs();
1147    let mut nodes = Vec::with_capacity(3);
1148
1149    let text_pos = if let Some(label) = label {
1150        let text_bounds = limits.resolve(width, Length::Shrink, Size::INFINITY);
1151        let state = tree.state.downcast_mut::<State>();
1152        let label_paragraph = &mut state.label;
1153        label_paragraph.update(Text {
1154            content: label,
1155            font,
1156            bounds: text_bounds,
1157            size: iced::Pixels(size.unwrap_or_else(|| renderer.default_size().0)),
1158            horizontal_alignment: alignment::Horizontal::Left,
1159            vertical_alignment: alignment::Vertical::Center,
1160            line_height,
1161            shaping: text::Shaping::Advanced,
1162            wrapping: text::Wrapping::None,
1163        });
1164        let label_size = label_paragraph.min_bounds();
1165
1166        nodes.push(layout::Node::new(label_size));
1167        Vector::new(0.0, label_size.height + f32::from(spacing))
1168    } else {
1169        Vector::ZERO
1170    };
1171
1172    let text_size = size.unwrap_or_else(|| renderer.default_size().0);
1173    let mut text_input_height = line_height.to_absolute(text_size.into()).0;
1174    let padding = padding.fit(Size::ZERO, limits.max());
1175
1176    let helper_pos = if leading_icon.is_some() || trailing_icon.is_some() {
1177        let children = &mut tree.children;
1178        let limits_copy = limits;
1180
1181        let limits = limits.shrink(padding);
1182        let icon_spacing = 8.0;
1183        let mut c_i = 0;
1184        let (leading_icon_width, mut leading_icon) =
1185            if let Some((icon, tree)) = leading_icon.zip(children.get_mut(c_i)) {
1186                let size = icon.as_widget().size();
1187                let icon_node = icon.as_widget().layout(
1188                    tree,
1189                    renderer,
1190                    &Limits::NONE.width(size.width).height(size.height),
1191                );
1192                text_input_height = text_input_height.max(icon_node.bounds().height);
1193                c_i += 1;
1194                (icon_node.bounds().width + icon_spacing, Some(icon_node))
1195            } else {
1196                (0.0, None)
1197            };
1198
1199        let (trailing_icon_width, mut trailing_icon) =
1200            if let Some((icon, tree)) = trailing_icon.zip(children.get_mut(c_i)) {
1201                let size = icon.as_widget().size();
1202                let icon_node = icon.as_widget().layout(
1203                    tree,
1204                    renderer,
1205                    &Limits::NONE.width(size.width).height(size.height),
1206                );
1207                text_input_height = text_input_height.max(icon_node.bounds().height);
1208                (icon_node.bounds().width + icon_spacing, Some(icon_node))
1209            } else {
1210                (0.0, None)
1211            };
1212        let text_limits = limits
1213            .width(width)
1214            .height(line_height.to_absolute(text_size.into()));
1215        let text_bounds = text_limits.resolve(Length::Shrink, Length::Shrink, Size::INFINITY);
1216        let text_node = layout::Node::new(
1217            text_bounds - Size::new(leading_icon_width + trailing_icon_width, 0.0),
1218        )
1219        .move_to(Point::new(
1220            padding.left + leading_icon_width,
1221            padding.top
1222                + ((text_input_height - line_height.to_absolute(text_size.into()).0) / 2.0)
1223                    .max(0.0),
1224        ));
1225        let mut node_list: Vec<_> = Vec::with_capacity(3);
1226
1227        let text_node_bounds = text_node.bounds();
1228        node_list.push(text_node);
1229
1230        if let Some(leading_icon) = leading_icon.take() {
1231            node_list.push(leading_icon.clone().move_to(Point::new(
1232                padding.left,
1233                padding.top + ((text_input_height - leading_icon.bounds().height) / 2.0).max(0.0),
1234            )));
1235        }
1236        if let Some(trailing_icon) = trailing_icon.take() {
1237            let trailing_icon = trailing_icon.clone().move_to(Point::new(
1238                text_node_bounds.x + text_node_bounds.width + f32::from(spacing),
1239                padding.top + ((text_input_height - trailing_icon.bounds().height) / 2.0).max(0.0),
1240            ));
1241            node_list.push(trailing_icon);
1242        }
1243
1244        let text_input_size = Size::new(
1245            text_node_bounds.x + text_node_bounds.width + trailing_icon_width,
1246            text_input_height,
1247        )
1248        .expand(padding);
1249
1250        let input_limits = limits_copy
1251            .width(width)
1252            .height(text_input_height.max(text_input_size.height))
1253            .min_width(text_input_size.width);
1254        let input_bounds = input_limits.resolve(
1255            width,
1256            text_input_height.max(text_input_size.height),
1257            text_input_size,
1258        );
1259        let input_node = layout::Node::with_children(input_bounds, node_list).translate(text_pos);
1260        let y_pos = input_node.bounds().y + input_node.bounds().height + f32::from(spacing);
1261        nodes.push(input_node);
1262
1263        Vector::new(0.0, y_pos)
1264    } else {
1265        let limits = limits
1266            .width(width)
1267            .height(text_input_height + padding.vertical())
1268            .shrink(padding);
1269        let text_bounds = limits.resolve(Length::Shrink, Length::Shrink, Size::INFINITY);
1270
1271        let text = layout::Node::new(text_bounds).move_to(Point::new(padding.left, padding.top));
1272
1273        let node = layout::Node::with_children(text_bounds.expand(padding), vec![text])
1274            .translate(text_pos);
1275        let y_pos = node.bounds().y + node.bounds().height + f32::from(spacing);
1276
1277        nodes.push(node);
1278
1279        Vector::new(0.0, y_pos)
1280    };
1281
1282    if let Some(helper_text) = helper_text {
1283        let limits = limits
1284            .width(width)
1285            .shrink(padding)
1286            .height(helper_text_line_height.to_absolute(helper_text_size.into()));
1287        let text_bounds = limits.resolve(width, Length::Shrink, Size::INFINITY);
1288        let state = tree.state.downcast_mut::<State>();
1289        let helper_text_paragraph = &mut state.helper_text;
1290        helper_text_paragraph.update(Text {
1291            content: helper_text,
1292            font,
1293            bounds: text_bounds,
1294            size: iced::Pixels(helper_text_size),
1295            horizontal_alignment: alignment::Horizontal::Left,
1296            vertical_alignment: alignment::Vertical::Center,
1297            line_height: helper_text_line_height,
1298            shaping: text::Shaping::Advanced,
1299            wrapping: text::Wrapping::None,
1300        });
1301        let helper_text_size = helper_text_paragraph.min_bounds();
1302        let helper_text_node = layout::Node::new(helper_text_size).translate(helper_pos);
1303        nodes.push(helper_text_node);
1304    };
1305
1306    let mut size = nodes.iter().fold(Size::ZERO, |size, node| {
1307        Size::new(
1308            size.width.max(node.bounds().width),
1309            size.height + node.bounds().height,
1310        )
1311    });
1312    size.height += (nodes.len() - 1) as f32 * f32::from(spacing);
1313
1314    let limits = limits
1315        .width(width)
1316        .height(size.height)
1317        .min_width(size.width);
1318
1319    layout::Node::with_children(limits.resolve(width, size.height, size), nodes)
1320}
1321
1322#[allow(clippy::too_many_arguments)]
1326#[allow(clippy::too_many_lines)]
1327#[allow(clippy::missing_panics_doc)]
1328#[allow(clippy::cast_lossless)]
1329#[allow(clippy::cast_possible_truncation)]
1330pub fn update<'a, Message: Clone + 'static>(
1331    id: Option<Id>,
1332    event: Event,
1333    text_layout: Layout<'_>,
1334    edit_button_layout: Option<Layout<'_>>,
1335    cursor: mouse::Cursor,
1336    clipboard: &mut dyn Clipboard,
1337    shell: &mut Shell<'_, Message>,
1338    value: &mut Value,
1339    size: f32,
1340    font: <crate::Renderer as iced_core::text::Renderer>::Font,
1341    is_editable_variant: bool,
1342    is_secure: bool,
1343    on_focus: Option<&Message>,
1344    on_unfocus: Option<&Message>,
1345    on_input: Option<&dyn Fn(String) -> Message>,
1346    on_paste: Option<&dyn Fn(String) -> Message>,
1347    on_submit: Option<&dyn Fn(String) -> Message>,
1348    on_tab: Option<&Message>,
1349    on_toggle_edit: Option<&dyn Fn(bool) -> Message>,
1350    state: impl FnOnce() -> &'a mut State,
1351    #[allow(unused_variables)] on_start_dnd_source: Option<&dyn Fn(State) -> Message>,
1352    #[allow(unused_variables)] dnd_id: u128,
1353    line_height: text::LineHeight,
1354    layout: Layout<'_>,
1355    manage_value: bool,
1356    drag_threshold: f32,
1357) -> event::Status {
1358    let update_cache = |state, value| {
1359        replace_paragraph(
1360            state,
1361            layout,
1362            value,
1363            font,
1364            iced::Pixels(size),
1365            line_height,
1366            &Limits::NONE.max_width(text_layout.bounds().width),
1367        );
1368    };
1369
1370    let mut secured_value = if is_secure {
1371        value.secure()
1372    } else {
1373        value.clone()
1374    };
1375    let unsecured_value = value;
1376    let value = &mut secured_value;
1377
1378    #[inline]
1382    #[cold]
1383    fn cold() {}
1384
1385    match event {
1386        Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
1387        | Event::Touch(touch::Event::FingerPressed { .. }) => {
1388            cold();
1389            let state = state();
1390
1391            let click_position = if on_input.is_some() || manage_value {
1392                cursor.position_over(layout.bounds())
1393            } else {
1394                None
1395            };
1396
1397            if let Some(cursor_position) = click_position {
1398                if state.dragging_state.is_none()
1400                    && edit_button_layout.is_some_and(|l| cursor.is_over(l.bounds()))
1401                {
1402                    if is_editable_variant {
1403                        state.is_read_only = !state.is_read_only;
1404                        state.move_cursor_to_end();
1405
1406                        if let Some(on_toggle_edit) = on_toggle_edit {
1407                            shell.publish(on_toggle_edit(!state.is_read_only));
1408                        }
1409
1410                        let now = Instant::now();
1411                        LAST_FOCUS_UPDATE.with(|x| x.set(now));
1412                        state.is_focused = Some(Focus {
1413                            updated_at: now,
1414                            now,
1415                            focused: true,
1416                            needs_update: false,
1417                        });
1418                    }
1419
1420                    return event::Status::Captured;
1421                }
1422
1423                let target = cursor_position.x - text_layout.bounds().x;
1424
1425                let click =
1426                    mouse::Click::new(cursor_position, mouse::Button::Left, state.last_click);
1427
1428                match (
1429                    &state.dragging_state,
1430                    click.kind(),
1431                    state.cursor().state(value),
1432                ) {
1433                    #[cfg(feature = "wayland")]
1434                    (None, click::Kind::Single, cursor::State::Selection { start, end }) => {
1435                        let left = start.min(end);
1436                        let right = end.max(start);
1437
1438                        let (left_position, _left_offset) = measure_cursor_and_scroll_offset(
1439                            state.value.raw(),
1440                            text_layout.bounds(),
1441                            left,
1442                        );
1443
1444                        let (right_position, _right_offset) = measure_cursor_and_scroll_offset(
1445                            state.value.raw(),
1446                            text_layout.bounds(),
1447                            right,
1448                        );
1449
1450                        let width = right_position - left_position;
1451                        let selection_bounds = Rectangle {
1452                            x: text_layout.bounds().x + left_position,
1453                            y: text_layout.bounds().y,
1454                            width,
1455                            height: text_layout.bounds().height,
1456                        };
1457
1458                        if cursor.is_over(selection_bounds) && (on_input.is_some() || manage_value)
1459                        {
1460                            state.dragging_state = Some(DraggingState::PrepareDnd(cursor_position));
1461                            return event::Status::Captured;
1462                        }
1463                        update_cache(state, value);
1465                        state.setting_selection(value, text_layout.bounds(), target);
1466                        state.dragging_state = None;
1467                        return event::Status::Captured;
1468                    }
1469                    (None, click::Kind::Single, _) => {
1470                        state.setting_selection(value, text_layout.bounds(), target);
1471                    }
1472                    (None | Some(DraggingState::Selection), click::Kind::Double, _) => {
1473                        update_cache(state, value);
1474
1475                        if is_secure {
1476                            state.cursor.select_all(value);
1477                        } else {
1478                            let position =
1479                                find_cursor_position(text_layout.bounds(), value, state, target)
1480                                    .unwrap_or(0);
1481
1482                            state.cursor.select_range(
1483                                value.previous_start_of_word(position),
1484                                value.next_end_of_word(position),
1485                            );
1486                        }
1487                        state.dragging_state = Some(DraggingState::Selection);
1488                    }
1489                    (None | Some(DraggingState::Selection), click::Kind::Triple, _) => {
1490                        update_cache(state, value);
1491                        state.cursor.select_all(value);
1492                        state.dragging_state = Some(DraggingState::Selection);
1493                    }
1494                    _ => {
1495                        state.dragging_state = None;
1496                    }
1497                }
1498
1499                if !state.is_focused()
1501                    && matches!(state.dragging_state, None | Some(DraggingState::Selection))
1502                {
1503                    if let Some(on_focus) = on_focus {
1504                        shell.publish(on_focus.clone());
1505                    }
1506
1507                    if state.is_read_only {
1508                        state.is_read_only = false;
1509                        if let Some(on_toggle_edit) = on_toggle_edit {
1510                            let message = (on_toggle_edit)(true);
1511                            shell.publish(message);
1512                        }
1513                    }
1514
1515                    let now = Instant::now();
1516                    LAST_FOCUS_UPDATE.with(|x| x.set(now));
1517
1518                    state.is_focused = Some(Focus {
1519                        updated_at: now,
1520                        now,
1521                        focused: true,
1522                        needs_update: false,
1523                    });
1524                }
1525
1526                state.last_click = Some(click);
1527
1528                return event::Status::Captured;
1529            } else {
1530                state.unfocus();
1531
1532                if let Some(on_unfocus) = on_unfocus {
1533                    shell.publish(on_unfocus.clone());
1534                }
1535            }
1536        }
1537        Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
1538        | Event::Touch(touch::Event::FingerLifted { .. } | touch::Event::FingerLost { .. }) => {
1539            cold();
1540            let state = state();
1541            #[cfg(feature = "wayland")]
1542            if matches!(state.dragging_state, Some(DraggingState::PrepareDnd(_))) {
1543                update_cache(state, value);
1545                if let Some(position) = cursor.position_over(layout.bounds()) {
1546                    let target = position.x - text_layout.bounds().x;
1547                    state.setting_selection(value, text_layout.bounds(), target);
1548                }
1549            }
1550            state.dragging_state = None;
1551
1552            return if cursor.is_over(layout.bounds()) {
1553                event::Status::Captured
1554            } else {
1555                event::Status::Ignored
1556            };
1557        }
1558        Event::Mouse(mouse::Event::CursorMoved { position })
1559        | Event::Touch(touch::Event::FingerMoved { position, .. }) => {
1560            let state = state();
1561
1562            if matches!(state.dragging_state, Some(DraggingState::Selection)) {
1563                let target = position.x - text_layout.bounds().x;
1564
1565                update_cache(state, value);
1566                let position =
1567                    find_cursor_position(text_layout.bounds(), value, state, target).unwrap_or(0);
1568
1569                state
1570                    .cursor
1571                    .select_range(state.cursor.start(value), position);
1572
1573                return event::Status::Captured;
1574            }
1575            #[cfg(feature = "wayland")]
1576            if let Some(DraggingState::PrepareDnd(start_position)) = state.dragging_state {
1577                let distance = ((position.x - start_position.x).powi(2)
1578                    + (position.y - start_position.y).powi(2))
1579                .sqrt();
1580
1581                if distance >= drag_threshold {
1582                    if is_secure {
1583                        return event::Status::Ignored;
1584                    }
1585
1586                    let input_text = state.selected_text(&value.to_string()).unwrap_or_default();
1587                    state.dragging_state =
1588                        Some(DraggingState::Dnd(DndAction::empty(), input_text.clone()));
1589                    let mut editor = Editor::new(unsecured_value, &mut state.cursor);
1590                    editor.delete();
1591
1592                    let contents = editor.contents();
1593                    let unsecured_value = Value::new(&contents);
1594                    state.tracked_value = unsecured_value.clone();
1595                    if let Some(on_input) = on_input {
1596                        let message = (on_input)(contents);
1597                        shell.publish(message);
1598                    }
1599                    if let Some(on_start_dnd) = on_start_dnd_source {
1600                        shell.publish(on_start_dnd(state.clone()));
1601                    }
1602                    let state_clone = state.clone();
1603
1604                    iced_core::clipboard::start_dnd(
1605                        clipboard,
1606                        false,
1607                        id.map(iced_core::clipboard::DndSource::Widget),
1608                        Some(iced_core::clipboard::IconSurface::new(
1609                            Element::from(
1610                                TextInput::<'static, ()>::new("", input_text.clone())
1611                                    .dnd_icon(true),
1612                            ),
1613                            iced_core::widget::tree::State::new(state_clone),
1614                            Vector::ZERO,
1615                        )),
1616                        Box::new(TextInputString(input_text)),
1617                        DndAction::Move,
1618                    );
1619
1620                    update_cache(state, &unsecured_value);
1621                } else {
1622                    state.dragging_state = Some(DraggingState::PrepareDnd(start_position));
1623                }
1624
1625                return event::Status::Captured;
1626            }
1627        }
1628        Event::Keyboard(keyboard::Event::KeyPressed {
1629            key,
1630            text,
1631            physical_key,
1632            modifiers,
1633            ..
1634        }) => {
1635            let state = state();
1636            state.keyboard_modifiers = modifiers;
1637
1638            if let Some(focus) = state.is_focused.as_mut().filter(|f| f.focused) {
1639                if state.is_read_only || (!manage_value && on_input.is_none()) {
1640                    return event::Status::Ignored;
1641                };
1642                let modifiers = state.keyboard_modifiers;
1643                focus.updated_at = Instant::now();
1644                LAST_FOCUS_UPDATE.with(|x| x.set(focus.updated_at));
1645
1646                if state.keyboard_modifiers == keyboard::Modifiers::COMMAND
1648                    || state.keyboard_modifiers
1649                        == keyboard::Modifiers::COMMAND | keyboard::Modifiers::CAPS_LOCK
1650                {
1651                    match key.as_ref() {
1652                        keyboard::Key::Character("c") | keyboard::Key::Character("C") => {
1653                            if !is_secure {
1654                                if let Some((start, end)) = state.cursor.selection(value) {
1655                                    clipboard.write(
1656                                        iced_core::clipboard::Kind::Standard,
1657                                        value.select(start, end).to_string(),
1658                                    );
1659                                }
1660                            }
1661                        }
1662                        keyboard::Key::Character("x") | keyboard::Key::Character("X") => {
1665                            if !is_secure {
1666                                if let Some((start, end)) = state.cursor.selection(value) {
1667                                    clipboard.write(
1668                                        iced_core::clipboard::Kind::Standard,
1669                                        value.select(start, end).to_string(),
1670                                    );
1671                                }
1672
1673                                let mut editor = Editor::new(value, &mut state.cursor);
1674                                editor.delete();
1675                                let content = editor.contents();
1676                                state.tracked_value = Value::new(&content);
1677                                if let Some(on_input) = on_input {
1678                                    let message = (on_input)(content);
1679                                    shell.publish(message);
1680                                }
1681                            }
1682                        }
1683                        keyboard::Key::Character("v") | keyboard::Key::Character("V") => {
1684                            let content = if let Some(content) = state.is_pasting.take() {
1685                                content
1686                            } else {
1687                                let content: String = clipboard
1688                                    .read(iced_core::clipboard::Kind::Standard)
1689                                    .unwrap_or_default()
1690                                    .chars()
1691                                    .filter(|c| !c.is_control())
1692                                    .collect();
1693
1694                                Value::new(&content)
1695                            };
1696
1697                            let mut editor = Editor::new(unsecured_value, &mut state.cursor);
1698
1699                            editor.paste(content.clone());
1700
1701                            let contents = editor.contents();
1702                            let unsecured_value = Value::new(&contents);
1703                            state.tracked_value = unsecured_value.clone();
1704
1705                            if let Some(on_input) = on_input {
1706                                let message = if let Some(paste) = &on_paste {
1707                                    (paste)(contents)
1708                                } else {
1709                                    (on_input)(contents)
1710                                };
1711
1712                                shell.publish(message);
1713                            }
1714
1715                            state.is_pasting = Some(content);
1716
1717                            let value = if is_secure {
1718                                unsecured_value.secure()
1719                            } else {
1720                                unsecured_value
1721                            };
1722
1723                            update_cache(state, &value);
1724                            return event::Status::Captured;
1725                        }
1726
1727                        keyboard::Key::Character("a") | keyboard::Key::Character("A") => {
1728                            state.cursor.select_all(value);
1729                            return event::Status::Captured;
1730                        }
1731
1732                        _ => {}
1733                    }
1734                }
1735
1736                if let Some(c) = text.and_then(|t| t.chars().next().filter(|c| !c.is_control())) {
1738                    if state.is_read_only || (!manage_value && on_input.is_none()) {
1739                        return event::Status::Ignored;
1740                    };
1741
1742                    state.is_pasting = None;
1743
1744                    if !state.keyboard_modifiers.command() && !modifiers.control() {
1745                        let mut editor = Editor::new(unsecured_value, &mut state.cursor);
1746
1747                        editor.insert(c);
1748
1749                        let contents = editor.contents();
1750                        let unsecured_value = Value::new(&contents);
1751                        state.tracked_value = unsecured_value.clone();
1752
1753                        if let Some(on_input) = on_input {
1754                            let message = (on_input)(contents);
1755                            shell.publish(message);
1756                        }
1757
1758                        focus.updated_at = Instant::now();
1759                        LAST_FOCUS_UPDATE.with(|x| x.set(focus.updated_at));
1760
1761                        let value = if is_secure {
1762                            unsecured_value.secure()
1763                        } else {
1764                            unsecured_value
1765                        };
1766
1767                        update_cache(state, &value);
1768
1769                        return event::Status::Captured;
1770                    }
1771                }
1772
1773                match key.as_ref() {
1774                    keyboard::Key::Named(keyboard::key::Named::Enter) => {
1775                        if let Some(on_submit) = on_submit {
1776                            shell.publish((on_submit)(unsecured_value.to_string()));
1777                        }
1778                    }
1779                    keyboard::Key::Named(keyboard::key::Named::Backspace) => {
1780                        if platform::is_jump_modifier_pressed(modifiers)
1781                            && state.cursor.selection(value).is_none()
1782                        {
1783                            if is_secure {
1784                                let cursor_pos = state.cursor.end(value);
1785                                state.cursor.select_range(0, cursor_pos);
1786                            } else {
1787                                state.cursor.select_left_by_words(value);
1788                            }
1789                        }
1790
1791                        let mut editor = Editor::new(unsecured_value, &mut state.cursor);
1792                        editor.backspace();
1793
1794                        let contents = editor.contents();
1795                        let unsecured_value = Value::new(&contents);
1796                        state.tracked_value = unsecured_value.clone();
1797                        if let Some(on_input) = on_input {
1798                            let message = (on_input)(editor.contents());
1799                            shell.publish(message);
1800                        }
1801                        let value = if is_secure {
1802                            unsecured_value.secure()
1803                        } else {
1804                            unsecured_value
1805                        };
1806                        update_cache(state, &value);
1807                    }
1808                    keyboard::Key::Named(keyboard::key::Named::Delete) => {
1809                        if platform::is_jump_modifier_pressed(modifiers)
1810                            && state.cursor.selection(value).is_none()
1811                        {
1812                            if is_secure {
1813                                let cursor_pos = state.cursor.end(unsecured_value);
1814                                state.cursor.select_range(cursor_pos, unsecured_value.len());
1815                            } else {
1816                                state.cursor.select_right_by_words(unsecured_value);
1817                            }
1818                        }
1819
1820                        let mut editor = Editor::new(unsecured_value, &mut state.cursor);
1821                        editor.delete();
1822                        let contents = editor.contents();
1823                        let unsecured_value = Value::new(&contents);
1824                        if let Some(on_input) = on_input {
1825                            let message = (on_input)(contents);
1826                            state.tracked_value = unsecured_value.clone();
1827                            shell.publish(message);
1828                        }
1829
1830                        let value = if is_secure {
1831                            unsecured_value.secure()
1832                        } else {
1833                            unsecured_value
1834                        };
1835
1836                        update_cache(state, &value);
1837                    }
1838                    keyboard::Key::Named(keyboard::key::Named::ArrowLeft) => {
1839                        if platform::is_jump_modifier_pressed(modifiers) && !is_secure {
1840                            if modifiers.shift() {
1841                                state.cursor.select_left_by_words(value);
1842                            } else {
1843                                state.cursor.move_left_by_words(value);
1844                            }
1845                        } else if modifiers.shift() {
1846                            state.cursor.select_left(value);
1847                        } else {
1848                            state.cursor.move_left(value);
1849                        }
1850                    }
1851                    keyboard::Key::Named(keyboard::key::Named::ArrowRight) => {
1852                        if platform::is_jump_modifier_pressed(modifiers) && !is_secure {
1853                            if modifiers.shift() {
1854                                state.cursor.select_right_by_words(value);
1855                            } else {
1856                                state.cursor.move_right_by_words(value);
1857                            }
1858                        } else if modifiers.shift() {
1859                            state.cursor.select_right(value);
1860                        } else {
1861                            state.cursor.move_right(value);
1862                        }
1863                    }
1864                    keyboard::Key::Named(keyboard::key::Named::Home) => {
1865                        if modifiers.shift() {
1866                            state.cursor.select_range(state.cursor.start(value), 0);
1867                        } else {
1868                            state.cursor.move_to(0);
1869                        }
1870                    }
1871                    keyboard::Key::Named(keyboard::key::Named::End) => {
1872                        if modifiers.shift() {
1873                            state
1874                                .cursor
1875                                .select_range(state.cursor.start(value), value.len());
1876                        } else {
1877                            state.cursor.move_to(value.len());
1878                        }
1879                    }
1880                    keyboard::Key::Named(keyboard::key::Named::Escape) => {
1881                        state.unfocus();
1882                        state.is_read_only = true;
1883
1884                        if let Some(on_unfocus) = on_unfocus {
1885                            shell.publish(on_unfocus.clone());
1886                        }
1887                    }
1888
1889                    keyboard::Key::Named(keyboard::key::Named::Tab) => {
1890                        if let Some(on_tab) = on_tab {
1891                            shell.publish(on_tab.clone());
1895                        } else {
1896                            state.is_read_only = true;
1897
1898                            if let Some(on_unfocus) = on_unfocus {
1899                                shell.publish(on_unfocus.clone());
1900                            }
1901
1902                            return event::Status::Ignored;
1903                        };
1904                    }
1905
1906                    keyboard::Key::Named(
1907                        keyboard::key::Named::ArrowUp | keyboard::key::Named::ArrowDown,
1908                    ) => {
1909                        return event::Status::Ignored;
1910                    }
1911                    _ => {}
1912                }
1913
1914                return event::Status::Captured;
1915            }
1916        }
1917        Event::Keyboard(keyboard::Event::KeyReleased { key, .. }) => {
1918            let state = state();
1919
1920            if state.is_focused() {
1921                match key {
1922                    keyboard::Key::Character(c) if "v" == c => {
1923                        state.is_pasting = None;
1924                    }
1925                    keyboard::Key::Named(keyboard::key::Named::Tab)
1926                    | keyboard::Key::Named(keyboard::key::Named::ArrowUp)
1927                    | keyboard::Key::Named(keyboard::key::Named::ArrowDown) => {
1928                        return event::Status::Ignored;
1929                    }
1930                    _ => {}
1931                }
1932
1933                return event::Status::Captured;
1934            }
1935        }
1936        Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => {
1937            let state = state();
1938
1939            state.keyboard_modifiers = modifiers;
1940        }
1941        Event::Window(window::Event::RedrawRequested(now)) => {
1942            let state = state();
1943
1944            if let Some(focus) = state.is_focused.as_mut().filter(|f| f.focused) {
1945                focus.now = now;
1946
1947                let millis_until_redraw = CURSOR_BLINK_INTERVAL_MILLIS
1948                    - (now - focus.updated_at).as_millis() % CURSOR_BLINK_INTERVAL_MILLIS;
1949
1950                shell.request_redraw(window::RedrawRequest::At(
1951                    now + Duration::from_millis(u64::try_from(millis_until_redraw).unwrap()),
1952                ));
1953            }
1954        }
1955        #[cfg(feature = "wayland")]
1956        Event::Dnd(DndEvent::Source(SourceEvent::Finished | SourceEvent::Cancelled)) => {
1957            cold();
1958            let state = state();
1959            if matches!(state.dragging_state, Some(DraggingState::Dnd(..))) {
1960                state.dragging_state = None;
1962                return event::Status::Captured;
1963            }
1964        }
1965        #[cfg(feature = "wayland")]
1966        Event::Dnd(DndEvent::Offer(
1967            rectangle,
1968            OfferEvent::Enter {
1969                x,
1970                y,
1971                mime_types,
1972                surface,
1973            },
1974        )) if rectangle == Some(dnd_id) => {
1975            cold();
1976            let state = state();
1977            let is_clicked = text_layout.bounds().contains(Point {
1978                x: x as f32,
1979                y: y as f32,
1980            });
1981
1982            let mut accepted = false;
1983            for m in &mime_types {
1984                if SUPPORTED_TEXT_MIME_TYPES.contains(&m.as_str()) {
1985                    let clone = m.clone();
1986                    accepted = true;
1987                }
1988            }
1989            if accepted {
1990                let target = x as f32 - text_layout.bounds().x;
1991                state.dnd_offer =
1992                    DndOfferState::HandlingOffer(mime_types.clone(), DndAction::empty());
1993                let position = if target > 0.0 {
1995                    update_cache(state, value);
1996                    find_cursor_position(text_layout.bounds(), value, state, target)
1997                } else {
1998                    None
1999                };
2000
2001                state.cursor.move_to(position.unwrap_or(0));
2002                return event::Status::Captured;
2003            }
2004        }
2005        #[cfg(feature = "wayland")]
2006        Event::Dnd(DndEvent::Offer(rectangle, OfferEvent::Motion { x, y }))
2007            if rectangle == Some(dnd_id) =>
2008        {
2009            let state = state();
2010
2011            let target = x as f32 - text_layout.bounds().x;
2012            let position = if target > 0.0 {
2014                update_cache(state, value);
2015                find_cursor_position(text_layout.bounds(), value, state, target)
2016            } else {
2017                None
2018            };
2019
2020            state.cursor.move_to(position.unwrap_or(0));
2021            return event::Status::Captured;
2022        }
2023        #[cfg(feature = "wayland")]
2024        Event::Dnd(DndEvent::Offer(rectangle, OfferEvent::Drop)) if rectangle == Some(dnd_id) => {
2025            cold();
2026            let state = state();
2027            if let DndOfferState::HandlingOffer(mime_types, _action) = state.dnd_offer.clone() {
2028                let Some(mime_type) = SUPPORTED_TEXT_MIME_TYPES
2029                    .iter()
2030                    .find(|&&m| mime_types.iter().any(|t| t == m))
2031                else {
2032                    state.dnd_offer = DndOfferState::None;
2033                    return event::Status::Captured;
2034                };
2035                state.dnd_offer = DndOfferState::Dropped;
2036            }
2037
2038            return event::Status::Ignored;
2039        }
2040        #[cfg(feature = "wayland")]
2041        Event::Dnd(DndEvent::Offer(id, OfferEvent::LeaveDestination)) if Some(dnd_id) != id => {}
2042        #[cfg(feature = "wayland")]
2043        Event::Dnd(DndEvent::Offer(
2044            rectangle,
2045            OfferEvent::Leave | OfferEvent::LeaveDestination,
2046        )) => {
2047            cold();
2048            let state = state();
2049            match state.dnd_offer {
2052                DndOfferState::Dropped => {}
2053                _ => {
2054                    state.dnd_offer = DndOfferState::None;
2055                }
2056            };
2057            return event::Status::Captured;
2058        }
2059        #[cfg(feature = "wayland")]
2060        Event::Dnd(DndEvent::Offer(rectangle, OfferEvent::Data { data, mime_type }))
2061            if rectangle == Some(dnd_id) =>
2062        {
2063            cold();
2064            let state = state();
2065            if matches!(&state.dnd_offer, DndOfferState::Dropped) {
2066                state.dnd_offer = DndOfferState::None;
2067                if !SUPPORTED_TEXT_MIME_TYPES.contains(&mime_type.as_str()) || data.is_empty() {
2068                    return event::Status::Captured;
2069                }
2070                let Ok(content) = String::from_utf8(data) else {
2071                    return event::Status::Captured;
2072                };
2073
2074                let mut editor = Editor::new(unsecured_value, &mut state.cursor);
2075
2076                editor.paste(Value::new(content.as_str()));
2077                let contents = editor.contents();
2078                let unsecured_value = Value::new(&contents);
2079                state.tracked_value = unsecured_value.clone();
2080                if let Some(on_paste) = on_paste.as_ref() {
2081                    let message = (on_paste)(contents);
2082                    shell.publish(message);
2083                }
2084
2085                let value = if is_secure {
2086                    unsecured_value.secure()
2087                } else {
2088                    unsecured_value
2089                };
2090                update_cache(state, &value);
2091                return event::Status::Captured;
2092            }
2093            return event::Status::Ignored;
2094        }
2095        _ => {}
2096    }
2097
2098    event::Status::Ignored
2099}
2100
2101#[allow(clippy::too_many_arguments)]
2106#[allow(clippy::too_many_lines)]
2107#[allow(clippy::missing_panics_doc)]
2108pub fn draw<'a, Message>(
2109    renderer: &mut crate::Renderer,
2110    theme: &crate::Theme,
2111    layout: Layout<'_>,
2112    text_layout: Layout<'_>,
2113    cursor_position: mouse::Cursor,
2114    tree: &Tree,
2115    value: &Value,
2116    placeholder: &str,
2117    size: Option<f32>,
2118    font: Option<<crate::Renderer as iced_core::text::Renderer>::Font>,
2119    is_disabled: bool,
2120    is_secure: bool,
2121    icon: Option<&Element<'a, Message, crate::Theme, crate::Renderer>>,
2122    trailing_icon: Option<&Element<'a, Message, crate::Theme, crate::Renderer>>,
2123    style: &<crate::Theme as StyleSheet>::Style,
2124    dnd_icon: bool,
2125    line_height: text::LineHeight,
2126    error: Option<&str>,
2127    label: Option<&str>,
2128    helper_text: Option<&str>,
2129    helper_text_size: f32,
2130    helper_line_height: text::LineHeight,
2131    viewport: &Rectangle,
2132    renderer_style: &renderer::Style,
2133) {
2134    let children = &tree.children;
2136
2137    let state = tree.state.downcast_ref::<State>();
2138    let secure_value = is_secure.then(|| value.secure());
2139    let value = secure_value.as_ref().unwrap_or(value);
2140
2141    let mut children_layout = layout.children();
2142
2143    let (label_layout, layout, helper_text_layout) = if label.is_some() && helper_text.is_some() {
2144        let label_layout = children_layout.next();
2145        let layout = children_layout.next().unwrap();
2146        let helper_text_layout = children_layout.next();
2147        (label_layout, layout, helper_text_layout)
2148    } else if label.is_some() {
2149        let label_layout = children_layout.next();
2150        let layout = children_layout.next().unwrap();
2151        (label_layout, layout, None)
2152    } else if helper_text.is_some() {
2153        let layout = children_layout.next().unwrap();
2154        let helper_text_layout = children_layout.next();
2155        (None, layout, helper_text_layout)
2156    } else {
2157        let layout = children_layout.next().unwrap();
2158
2159        (None, layout, None)
2160    };
2161
2162    let mut children_layout = layout.children();
2163    let bounds = layout.bounds();
2164    let text_bounds = children_layout.next().unwrap_or(text_layout).bounds();
2166
2167    let is_mouse_over = cursor_position.is_over(bounds);
2168
2169    let appearance = if is_disabled {
2170        theme.disabled(style)
2171    } else if error.is_some() {
2172        theme.error(style)
2173    } else if state.is_focused() {
2174        theme.focused(style)
2175    } else if is_mouse_over {
2176        theme.hovered(style)
2177    } else {
2178        theme.active(style)
2179    };
2180
2181    let mut icon_color = appearance.icon_color.unwrap_or(renderer_style.icon_color);
2182    let mut text_color = appearance.text_color.unwrap_or(renderer_style.text_color);
2183
2184    if is_disabled {
2186        let background = theme.current_container().component.base.into();
2187        icon_color = icon_color.blend_alpha(background, 0.5);
2188        text_color = text_color.blend_alpha(background, 0.5);
2189    }
2190
2191    if let Some(border_offset) = appearance.border_offset {
2193        let offset_bounds = Rectangle {
2194            x: bounds.x - border_offset,
2195            y: bounds.y - border_offset,
2196            width: border_offset.mul_add(2.0, bounds.width),
2197            height: border_offset.mul_add(2.0, bounds.height),
2198        };
2199        renderer.fill_quad(
2200            renderer::Quad {
2201                bounds,
2202                border: Border {
2203                    radius: appearance.border_radius,
2204                    width: appearance.border_width,
2205                    ..Default::default()
2206                },
2207                shadow: Shadow {
2208                    offset: Vector::new(0.0, 1.0),
2209                    color: Color::TRANSPARENT,
2210                    blur_radius: 0.0,
2211                },
2212            },
2213            appearance.background,
2214        );
2215        renderer.fill_quad(
2216            renderer::Quad {
2217                bounds: offset_bounds,
2218                border: Border {
2219                    width: appearance.border_width,
2220                    color: appearance.border_color,
2221                    radius: appearance.border_radius,
2222                },
2223                shadow: Shadow {
2224                    offset: Vector::new(0.0, 1.0),
2225                    color: Color::TRANSPARENT,
2226                    blur_radius: 0.0,
2227                },
2228            },
2229            Background::Color(Color::TRANSPARENT),
2230        );
2231    } else {
2232        renderer.fill_quad(
2233            renderer::Quad {
2234                bounds,
2235                border: Border {
2236                    width: appearance.border_width,
2237                    color: appearance.border_color,
2238                    radius: appearance.border_radius,
2239                },
2240                shadow: Shadow {
2241                    offset: Vector::new(0.0, 1.0),
2242                    color: Color::TRANSPARENT,
2243                    blur_radius: 0.0,
2244                },
2245            },
2246            appearance.background,
2247        );
2248    }
2249
2250    if let (Some(label_layout), Some(label)) = (label_layout, label) {
2252        renderer.fill_text(
2253            Text {
2254                content: label.to_string(),
2255                size: iced::Pixels(size.unwrap_or_else(|| renderer.default_size().0)),
2256                font: font.unwrap_or_else(|| renderer.default_font()),
2257                bounds: label_layout.bounds().size(),
2258                horizontal_alignment: alignment::Horizontal::Left,
2259                vertical_alignment: alignment::Vertical::Top,
2260                line_height,
2261                shaping: text::Shaping::Advanced,
2262                wrapping: text::Wrapping::None,
2263            },
2264            label_layout.bounds().position(),
2265            appearance.label_color,
2266            *viewport,
2267        );
2268    }
2269    let mut child_index = 0;
2270    let leading_icon_tree = children.get(child_index);
2271    let has_start_icon = icon.is_some();
2273    if let (Some(icon), Some(tree)) = (icon, leading_icon_tree) {
2274        let mut children = text_layout.children();
2275        let _ = children.next().unwrap();
2276        let icon_layout = children.next().unwrap();
2277
2278        icon.as_widget().draw(
2279            tree,
2280            renderer,
2281            theme,
2282            &renderer::Style {
2283                icon_color,
2284                text_color,
2285                scale_factor: renderer_style.scale_factor,
2286            },
2287            icon_layout,
2288            cursor_position,
2289            viewport,
2290        );
2291        child_index += 1;
2292    }
2293
2294    let text = value.to_string();
2295    let font = font.unwrap_or_else(|| renderer.default_font());
2296    let size = size.unwrap_or_else(|| renderer.default_size().0);
2297    let text_width = state.value.min_width();
2298    let actual_width = text_width.max(text_bounds.width);
2299
2300    let radius_0 = THEME.lock().unwrap().cosmic().corner_radii.radius_0.into();
2301    #[cfg(feature = "wayland")]
2302    let handling_dnd_offer = !matches!(state.dnd_offer, DndOfferState::None);
2303    #[cfg(not(feature = "wayland"))]
2304    let handling_dnd_offer = false;
2305    let (cursor, offset) = if let Some(focus) =
2306        state.is_focused.filter(|f| f.focused).or_else(|| {
2307            let now = Instant::now();
2308            handling_dnd_offer.then_some(Focus {
2309                needs_update: false,
2310                updated_at: now,
2311                now,
2312                focused: true,
2313            })
2314        }) {
2315        match state.cursor.state(value) {
2316            cursor::State::Index(position) => {
2317                let (text_value_width, offset) =
2318                    measure_cursor_and_scroll_offset(state.value.raw(), text_bounds, position);
2319
2320                let is_cursor_visible = handling_dnd_offer
2321                    || ((focus.now - focus.updated_at).as_millis() / CURSOR_BLINK_INTERVAL_MILLIS)
2322                        % 2
2323                        == 0;
2324                if is_cursor_visible {
2325                    if dnd_icon {
2326                        (None, 0.0)
2327                    } else {
2328                        (
2329                            Some((
2330                                renderer::Quad {
2331                                    bounds: Rectangle {
2332                                        x: text_bounds.x + text_value_width - offset
2333                                            + if text_value_width < 0. {
2334                                                actual_width
2335                                            } else {
2336                                                0.
2337                                            },
2338                                        y: text_bounds.y,
2339                                        width: 1.0,
2340                                        height: text_bounds.height,
2341                                    },
2342                                    border: Border {
2343                                        width: 0.0,
2344                                        color: Color::TRANSPARENT,
2345                                        radius: radius_0,
2346                                    },
2347                                    shadow: Shadow {
2348                                        offset: Vector::ZERO,
2349                                        color: Color::TRANSPARENT,
2350                                        blur_radius: 0.0,
2351                                    },
2352                                },
2353                                text_color,
2354                            )),
2355                            offset,
2356                        )
2357                    }
2358                } else {
2359                    (None, offset)
2360                }
2361            }
2362            cursor::State::Selection { start, end } => {
2363                let left = start.min(end);
2364                let right = end.max(start);
2365
2366                let value_paragraph = &state.value;
2367                let (left_position, left_offset) =
2368                    measure_cursor_and_scroll_offset(value_paragraph.raw(), text_bounds, left);
2369
2370                let (right_position, right_offset) =
2371                    measure_cursor_and_scroll_offset(value_paragraph.raw(), text_bounds, right);
2372
2373                let width = right_position - left_position;
2374                if dnd_icon {
2375                    (None, 0.0)
2376                } else {
2377                    (
2378                        Some((
2379                            renderer::Quad {
2380                                bounds: Rectangle {
2381                                    x: text_bounds.x
2382                                        + left_position
2383                                        + if left_position < 0. || right_position < 0. {
2384                                            actual_width
2385                                        } else {
2386                                            0.
2387                                        },
2388                                    y: text_bounds.y,
2389                                    width,
2390                                    height: text_bounds.height,
2391                                },
2392                                border: Border {
2393                                    width: 0.0,
2394                                    color: Color::TRANSPARENT,
2395                                    radius: radius_0,
2396                                },
2397                                shadow: Shadow {
2398                                    offset: Vector::ZERO,
2399                                    color: Color::TRANSPARENT,
2400                                    blur_radius: 0.0,
2401                                },
2402                            },
2403                            appearance.selected_fill,
2404                        )),
2405                        if end == right {
2406                            right_offset
2407                        } else {
2408                            left_offset
2409                        },
2410                    )
2411                }
2412            }
2413        }
2414    } else {
2415        (None, 0.0)
2416    };
2417
2418    let render = |renderer: &mut crate::Renderer| {
2419        if let Some((cursor, color)) = cursor {
2420            renderer.fill_quad(cursor, color);
2421        } else {
2422            renderer.with_translation(Vector::ZERO, |_| {});
2423        }
2424
2425        let bounds = Rectangle {
2426            x: text_bounds.x - offset,
2427            y: text_bounds.center_y(),
2428            width: actual_width,
2429            ..text_bounds
2430        };
2431        let color = if text.is_empty() {
2432            appearance.placeholder_color
2433        } else {
2434            text_color
2435        };
2436
2437        renderer.fill_text(
2438            Text {
2439                content: if text.is_empty() {
2440                    placeholder.to_string()
2441                } else {
2442                    text.clone()
2443                },
2444                font,
2445                bounds: bounds.size(),
2446                size: iced::Pixels(size),
2447                horizontal_alignment: alignment::Horizontal::Left,
2448                vertical_alignment: alignment::Vertical::Center,
2449                line_height: text::LineHeight::default(),
2450                shaping: text::Shaping::Advanced,
2451                wrapping: text::Wrapping::None,
2452            },
2453            bounds.position(),
2454            color,
2455            *viewport,
2456        );
2457    };
2458
2459    renderer.with_layer(text_bounds, render);
2460
2461    let trailing_icon_tree = children.get(child_index);
2462
2463    if let (Some(icon), Some(tree)) = (trailing_icon, trailing_icon_tree) {
2465        let mut children = text_layout.children();
2466        let mut icon_layout = children.next().unwrap();
2467        if has_start_icon {
2468            icon_layout = children.next().unwrap();
2469        }
2470        icon_layout = children.next().unwrap();
2471
2472        icon.as_widget().draw(
2473            tree,
2474            renderer,
2475            theme,
2476            &renderer::Style {
2477                icon_color,
2478                text_color,
2479                scale_factor: renderer_style.scale_factor,
2480            },
2481            icon_layout,
2482            cursor_position,
2483            viewport,
2484        );
2485    }
2486
2487    if let (Some(helper_text_layout), Some(helper_text)) = (helper_text_layout, helper_text) {
2489        renderer.fill_text(
2490            Text {
2491                content: helper_text.to_string(), size: iced::Pixels(helper_text_size),
2493                font,
2494                bounds: helper_text_layout.bounds().size(),
2495                horizontal_alignment: alignment::Horizontal::Left,
2496                vertical_alignment: alignment::Vertical::Top,
2497                line_height: helper_line_height,
2498                shaping: text::Shaping::Advanced,
2499                wrapping: text::Wrapping::None,
2500            },
2501            helper_text_layout.bounds().position(),
2502            text_color,
2503            *viewport,
2504        );
2505    }
2506}
2507
2508#[must_use]
2510pub fn mouse_interaction(
2511    layout: Layout<'_>,
2512    cursor_position: mouse::Cursor,
2513    is_disabled: bool,
2514) -> mouse::Interaction {
2515    if cursor_position.is_over(layout.bounds()) {
2516        if is_disabled {
2517            mouse::Interaction::NotAllowed
2518        } else {
2519            mouse::Interaction::Text
2520        }
2521    } else {
2522        mouse::Interaction::default()
2523    }
2524}
2525
2526#[derive(Debug, Clone)]
2528pub struct TextInputString(pub String);
2529
2530#[cfg(feature = "wayland")]
2531impl AsMimeTypes for TextInputString {
2532    fn available(&self) -> Cow<'static, [String]> {
2533        Cow::Owned(
2534            SUPPORTED_TEXT_MIME_TYPES
2535                .iter()
2536                .cloned()
2537                .map(String::from)
2538                .collect::<Vec<_>>(),
2539        )
2540    }
2541
2542    fn as_bytes(&self, mime_type: &str) -> Option<Cow<'static, [u8]>> {
2543        if SUPPORTED_TEXT_MIME_TYPES.contains(&mime_type) {
2544            Some(Cow::Owned(self.0.clone().into_bytes()))
2545        } else {
2546            None
2547        }
2548    }
2549}
2550
2551#[derive(Debug, Clone, PartialEq)]
2552pub(crate) enum DraggingState {
2553    Selection,
2554    #[cfg(feature = "wayland")]
2555    PrepareDnd(Point),
2556    #[cfg(feature = "wayland")]
2557    Dnd(DndAction, String),
2558}
2559
2560#[cfg(feature = "wayland")]
2561#[derive(Debug, Default, Clone)]
2562pub(crate) enum DndOfferState {
2563    #[default]
2564    None,
2565    HandlingOffer(Vec<String>, DndAction),
2566    Dropped,
2567}
2568#[derive(Debug, Default, Clone)]
2569#[cfg(not(feature = "wayland"))]
2570pub(crate) struct DndOfferState;
2571
2572#[derive(Debug, Default, Clone)]
2574#[must_use]
2575pub struct State {
2576    pub tracked_value: Value,
2577    pub value: crate::Plain,
2578    pub placeholder: crate::Plain,
2579    pub label: crate::Plain,
2580    pub helper_text: crate::Plain,
2581    pub dirty: bool,
2582    pub is_secure: bool,
2583    pub is_read_only: bool,
2584    pub emit_unfocus: bool,
2585    select_on_focus: bool,
2586    is_focused: Option<Focus>,
2587    dragging_state: Option<DraggingState>,
2588    dnd_offer: DndOfferState,
2589    is_pasting: Option<Value>,
2590    last_click: Option<mouse::Click>,
2591    cursor: Cursor,
2592    keyboard_modifiers: keyboard::Modifiers,
2593    }
2595
2596#[derive(Debug, Clone, Copy)]
2597struct Focus {
2598    updated_at: Instant,
2599    now: Instant,
2600    focused: bool,
2601    needs_update: bool,
2602}
2603
2604impl State {
2605    pub fn new(
2607        is_secure: bool,
2608        is_read_only: bool,
2609        always_active: bool,
2610        select_on_focus: bool,
2611    ) -> Self {
2612        Self {
2613            is_secure,
2614            is_read_only,
2615            is_focused: always_active.then(|| {
2616                let now = Instant::now();
2617                Focus {
2618                    updated_at: now,
2619                    now,
2620                    focused: true,
2621                    needs_update: false,
2622                }
2623            }),
2624            select_on_focus,
2625            ..Self::default()
2626        }
2627    }
2628
2629    #[must_use]
2631    pub fn selected_text(&self, text: &str) -> Option<String> {
2632        let value = Value::new(text);
2633        match self.cursor.state(&value) {
2634            cursor::State::Index(_) => None,
2635            cursor::State::Selection { start, end } => {
2636                let left = start.min(end);
2637                let right = end.max(start);
2638                Some(text[left..right].to_string())
2639            }
2640        }
2641    }
2642
2643    #[cfg(feature = "wayland")]
2644    #[must_use]
2646    pub fn dragged_text(&self) -> Option<String> {
2647        match self.dragging_state.as_ref() {
2648            Some(DraggingState::Dnd(_, text)) => Some(text.clone()),
2649            _ => None,
2650        }
2651    }
2652
2653    pub fn focused(is_secure: bool, is_read_only: bool) -> Self {
2655        Self {
2656            tracked_value: Value::default(),
2657            is_secure,
2658            value: crate::Plain::default(),
2659            placeholder: crate::Plain::default(),
2660            label: crate::Plain::default(),
2661            helper_text: crate::Plain::default(),
2662            is_read_only,
2663            emit_unfocus: false,
2664            is_focused: None,
2665            select_on_focus: false,
2666            dragging_state: None,
2667            dnd_offer: DndOfferState::default(),
2668            is_pasting: None,
2669            last_click: None,
2670            cursor: Cursor::default(),
2671            keyboard_modifiers: keyboard::Modifiers::default(),
2672            dirty: false,
2673        }
2674    }
2675
2676    #[inline]
2678    #[must_use]
2679    pub fn is_focused(&self) -> bool {
2680        self.is_focused.is_some_and(|f| f.focused)
2681    }
2682
2683    #[inline]
2685    #[must_use]
2686    pub fn cursor(&self) -> Cursor {
2687        self.cursor
2688    }
2689
2690    #[cold]
2692    pub fn focus(&mut self) {
2693        let now = Instant::now();
2694        LAST_FOCUS_UPDATE.with(|x| x.set(now));
2695        let was_focused = self.is_focused.is_some_and(|f| f.focused);
2696        self.is_read_only = false;
2697        self.is_focused = Some(Focus {
2698            updated_at: now,
2699            now,
2700            focused: true,
2701            needs_update: false,
2702        });
2703
2704        if was_focused {
2705            return;
2706        }
2707        if self.select_on_focus {
2708            self.select_all()
2709        } else {
2710            self.move_cursor_to_end();
2711        }
2712    }
2713
2714    #[cold]
2716    pub(super) fn unfocus(&mut self) {
2717        self.move_cursor_to_front();
2718        self.last_click = None;
2719        self.is_focused = self.is_focused.map(|mut f| {
2720            f.focused = false;
2721            f.needs_update = false;
2722            f
2723        });
2724        self.dragging_state = None;
2725        self.is_pasting = None;
2726        self.keyboard_modifiers = keyboard::Modifiers::default();
2727    }
2728
2729    #[inline]
2731    pub fn move_cursor_to_front(&mut self) {
2732        self.cursor.move_to(0);
2733    }
2734
2735    #[inline]
2737    pub fn move_cursor_to_end(&mut self) {
2738        self.cursor.move_to(usize::MAX);
2739    }
2740
2741    #[inline]
2743    pub fn move_cursor_to(&mut self, position: usize) {
2744        self.cursor.move_to(position);
2745    }
2746
2747    #[inline]
2749    pub fn select_all(&mut self) {
2750        self.cursor.select_range(0, usize::MAX);
2751    }
2752
2753    pub(super) fn setting_selection(&mut self, value: &Value, bounds: Rectangle<f32>, target: f32) {
2754        let position = if target > 0.0 {
2755            find_cursor_position(bounds, value, self, target)
2756        } else {
2757            None
2758        };
2759
2760        self.cursor.move_to(position.unwrap_or(0));
2761        self.dragging_state = Some(DraggingState::Selection);
2762    }
2763}
2764
2765impl operation::Focusable for State {
2766    #[inline]
2767    fn is_focused(&self) -> bool {
2768        Self::is_focused(self)
2769    }
2770
2771    #[inline]
2772    fn focus(&mut self) {
2773        Self::focus(self);
2774        if let Some(focus) = self.is_focused.as_mut() {
2775            focus.needs_update = true;
2776        }
2777    }
2778
2779    #[inline]
2780    fn unfocus(&mut self) {
2781        Self::unfocus(self);
2782        if let Some(focus) = self.is_focused.as_mut() {
2783            focus.needs_update = true;
2784        }
2785    }
2786}
2787
2788impl operation::TextInput for State {
2789    #[inline]
2790    fn move_cursor_to_front(&mut self) {
2791        Self::move_cursor_to_front(self);
2792    }
2793
2794    #[inline]
2795    fn move_cursor_to_end(&mut self) {
2796        Self::move_cursor_to_end(self);
2797    }
2798
2799    #[inline]
2800    fn move_cursor_to(&mut self, position: usize) {
2801        Self::move_cursor_to(self, position);
2802    }
2803
2804    #[inline]
2805    fn select_all(&mut self) {
2806        Self::select_all(self);
2807    }
2808}
2809
2810#[inline(never)]
2811fn measure_cursor_and_scroll_offset(
2812    paragraph: &impl text::Paragraph,
2813    text_bounds: Rectangle,
2814    cursor_index: usize,
2815) -> (f32, f32) {
2816    let grapheme_position = paragraph
2817        .grapheme_position(0, cursor_index)
2818        .unwrap_or(Point::ORIGIN);
2819
2820    let offset = ((grapheme_position.x + 5.0) - text_bounds.width).max(0.0);
2821
2822    (grapheme_position.x, offset)
2823}
2824
2825#[inline(never)]
2828fn find_cursor_position(
2829    text_bounds: Rectangle,
2830    value: &Value,
2831    state: &State,
2832    x: f32,
2833) -> Option<usize> {
2834    let offset = offset(text_bounds, value, state);
2835    let value = value.to_string();
2836
2837    let char_offset = state
2838        .value
2839        .raw()
2840        .hit_test(Point::new(x + offset, text_bounds.height / 2.0))
2841        .map(text::Hit::cursor)?;
2842
2843    Some(
2844        unicode_segmentation::UnicodeSegmentation::graphemes(
2845            &value[..char_offset.min(value.len())],
2846            true,
2847        )
2848        .count(),
2849    )
2850}
2851
2852#[inline(never)]
2853fn replace_paragraph(
2854    state: &mut State,
2855    layout: Layout<'_>,
2856    value: &Value,
2857    font: <crate::Renderer as iced_core::text::Renderer>::Font,
2858    text_size: Pixels,
2859    line_height: text::LineHeight,
2860    limits: &layout::Limits,
2861) {
2862    let mut children_layout = layout.children();
2863    let text_bounds = children_layout.next().unwrap();
2864    let bounds = limits.resolve(
2865        Length::Shrink,
2866        Length::Fill,
2867        Size::new(0., text_bounds.bounds().height),
2868    );
2869
2870    state.value = crate::Plain::new(Text {
2871        font,
2872        line_height,
2873        content: &value.to_string(),
2874        bounds,
2875        size: text_size,
2876        horizontal_alignment: alignment::Horizontal::Left,
2877        vertical_alignment: alignment::Vertical::Top,
2878        shaping: text::Shaping::Advanced,
2879        wrapping: text::Wrapping::None,
2880    });
2881}
2882
2883const CURSOR_BLINK_INTERVAL_MILLIS: u128 = 500;
2884
2885mod platform {
2886    use iced_core::keyboard;
2887
2888    #[inline]
2889    pub fn is_jump_modifier_pressed(modifiers: keyboard::Modifiers) -> bool {
2890        if cfg!(target_os = "macos") {
2891            modifiers.alt()
2892        } else {
2893            modifiers.control()
2894        }
2895    }
2896}
2897
2898#[inline(never)]
2899fn offset(text_bounds: Rectangle, value: &Value, state: &State) -> f32 {
2900    if state.is_focused() {
2901        let cursor = state.cursor();
2902
2903        let focus_position = match cursor.state(value) {
2904            cursor::State::Index(i) => i,
2905            cursor::State::Selection { end, .. } => end,
2906        };
2907
2908        let (_, offset) =
2909            measure_cursor_and_scroll_offset(state.value.raw(), text_bounds, focus_position);
2910
2911        offset
2912    } else {
2913        0.0
2914    }
2915}