Skip to main content

iced_widget/
text_editor.rs

1//! Text editors display a multi-line text input for text editing.
2//!
3//! # Example
4//! ```no_run
5//! # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; }
6//! # use iced::widget::Widget;
7//! #
8//! use iced::widget::text_editor;
9//!
10//! struct State {
11//!    content: text_editor::Content,
12//! }
13//!
14//! #[derive(Debug, Clone)]
15//! enum Message {
16//!     Edit(text_editor::Action)
17//! }
18//!
19//! fn view(state: &State) -> impl Widget<Message> {
20//!     text_editor(&state.content)
21//!         .placeholder("Type something here...")
22//!         .on_action(Message::Edit)
23//! }
24//!
25//! fn update(state: &mut State, message: Message) {
26//!     match message {
27//!         Message::Edit(action) => {
28//!             state.content.perform(action);
29//!         }
30//!     }
31//! }
32//! ```
33use crate::core::alignment;
34use crate::core::clipboard;
35use crate::core::layout::{self, Layout};
36use crate::core::mouse;
37use crate::core::renderer;
38use crate::core::text::editor::{self, Editor as _};
39use crate::core::text::highlighter;
40use crate::core::text::parser;
41use crate::core::text::{self, LineHeight, Text, Wrapping};
42use crate::core::theme;
43use crate::core::widget::{self, Widget};
44use crate::core::window;
45use crate::core::{
46    Background, Border, Color, Event, Font, Length, Padding, Pixels, Rectangle, Shell, Size, Theme,
47};
48
49use std::borrow::Cow;
50use std::cell::RefCell;
51use std::fmt;
52use std::ops::DerefMut;
53
54pub use text::Highlighter;
55pub use text::editor::{
56    Action, Binding, Cursor, Edit, KeyPress, Line, LineEnding, Motion, Selection,
57};
58
59/// A multi-line text input.
60///
61/// # Example
62/// ```no_run
63/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; }
64/// # use iced::widget::Widget;
65/// #
66/// use iced::widget::text_editor;
67///
68/// struct State {
69///    content: text_editor::Content,
70/// }
71///
72/// #[derive(Debug, Clone)]
73/// enum Message {
74///     Edit(text_editor::Action)
75/// }
76///
77/// fn view(state: &State) -> impl Widget<Message> {
78///     text_editor(&state.content)
79///         .placeholder("Type something here...")
80///         .on_action(Message::Edit)
81/// }
82///
83/// fn update(state: &mut State, message: Message) {
84///     match message {
85///         Message::Edit(action) => {
86///             state.content.perform(action);
87///         }
88///     }
89/// }
90/// ```
91pub struct TextEditor<'a, Parser, Message, Theme = crate::Theme, Renderer = crate::Renderer>
92where
93    Parser: text::Parser,
94    Theme: Catalog,
95    Renderer: text::Renderer,
96{
97    id: Option<widget::Id>,
98    content: &'a Content<Renderer>,
99    placeholder: Option<text::Fragment<'a>>,
100    font: Option<Font>,
101    text_size: Option<Pixels>,
102    line_height: Option<LineHeight>,
103    width: Length,
104    height: Length,
105    padding: Padding,
106    wrapping: Wrapping,
107    class: Theme::Class<'a>,
108    key_binding: Option<Box<dyn Fn(KeyPress) -> Option<Binding<Message>> + 'a>>,
109    on_edit: Option<Box<dyn Fn(Action) -> Message + 'a>>,
110    parser_settings: Parser::Settings,
111    highlighter: Option<Box<dyn text::Highlighter<Parser::Output, Theme> + 'a>>,
112    last_status: Option<Status>,
113}
114
115impl<'a, Message, Theme, Renderer> TextEditor<'a, parser::PlainText, Message, Theme, Renderer>
116where
117    Theme: Catalog,
118    Renderer: text::Renderer,
119{
120    /// Creates new [`TextEditor`] with the given [`Content`].
121    pub fn new(content: &'a Content<Renderer>) -> Self {
122        Self {
123            id: None,
124            content,
125            placeholder: None,
126            font: None,
127            text_size: None,
128            line_height: None,
129            width: Length::Fill,
130            height: Length::Fit,
131            padding: Padding::new(5.0),
132            wrapping: Wrapping::default(),
133            class: <Theme as Catalog>::default(),
134            key_binding: None,
135            on_edit: None,
136            parser_settings: (),
137            highlighter: None,
138            last_status: None,
139        }
140    }
141}
142
143impl<'a, Message, Renderer> TextEditor<'a, parser::PlainText, Message, crate::Theme, Renderer>
144where
145    Renderer: text::Renderer,
146{
147    /// Highlights the [`TextEditor`] using the given syntax.
148    ///
149    /// ```no_run
150    /// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; }
151    /// # use iced::widget::Widget;
152    /// #
153    /// use iced::color;
154    /// use iced::widget::text;
155    /// use iced::widget::text_editor;
156    /// use iced::Theme;
157    ///
158    /// struct State {
159    ///    content: text_editor::Content,
160    /// }
161    ///
162    /// fn view(state: &State) -> impl Widget<()> {
163    ///     text_editor(&state.content)
164    ///         .highlight("rust")
165    /// }
166    /// ```
167    #[cfg(feature = "highlighter")]
168    pub fn highlight(
169        self,
170        syntax: &str,
171    ) -> TextEditor<'a, iced_highlighter::Parser, Message, crate::Theme, Renderer>
172    where
173        Renderer: text::Renderer,
174    {
175        self.highlight_with(
176            iced_highlighter::Settings {
177                token: syntax.to_owned(),
178            },
179            crate::core::Code::highlight,
180        )
181    }
182}
183
184impl<'a, Parser, Message, Theme, Renderer> TextEditor<'a, Parser, Message, Theme, Renderer>
185where
186    Parser: text::Parser,
187    Theme: Catalog,
188    Renderer: text::Renderer,
189{
190    /// Sets the [`Id`](widget::Id) of the [`TextEditor`].
191    pub fn id(mut self, id: impl Into<widget::Id>) -> Self {
192        self.id = Some(id.into());
193        self
194    }
195
196    /// Sets the placeholder of the [`TextEditor`].
197    pub fn placeholder(mut self, placeholder: impl text::IntoFragment<'a>) -> Self {
198        self.placeholder = Some(placeholder.into_fragment());
199        self
200    }
201
202    /// Sets the width of the [`TextEditor`].
203    pub fn width(mut self, width: impl Into<Pixels>) -> Self {
204        self.width = Length::from(width.into());
205        self
206    }
207
208    /// Sets the height of the [`TextEditor`].
209    pub fn height(mut self, height: impl Into<Length>) -> Self {
210        self.height = height.into();
211        self
212    }
213
214    /// Sets the message that should be produced when some action is performed in
215    /// the [`TextEditor`].
216    ///
217    /// If this method is not called, the [`TextEditor`] will be disabled.
218    pub fn on_action(mut self, on_edit: impl Fn(Action) -> Message + 'a) -> Self {
219        self.on_edit = Some(Box::new(on_edit));
220        self
221    }
222
223    /// Sets the [`Font`] of the [`TextEditor`].
224    ///
225    /// [`Font`]: crate::core::Font
226    pub fn font(mut self, font: impl Into<Font>) -> Self {
227        self.font = Some(font.into());
228        self
229    }
230
231    /// Sets the text size of the [`TextEditor`].
232    pub fn size(mut self, size: impl Into<Pixels>) -> Self {
233        self.text_size = Some(size.into());
234        self
235    }
236
237    /// Sets the [`text::LineHeight`] of the [`TextEditor`].
238    pub fn line_height(mut self, line_height: impl Into<text::LineHeight>) -> Self {
239        self.line_height = Some(line_height.into());
240        self
241    }
242
243    /// Sets the [`Padding`] of the [`TextEditor`].
244    pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
245        self.padding = padding.into();
246        self
247    }
248
249    /// Sets the [`Wrapping`] strategy of the [`TextEditor`].
250    pub fn wrapping(mut self, wrapping: Wrapping) -> Self {
251        self.wrapping = wrapping;
252        self
253    }
254
255    /// Highlights the [`TextEditor`] with the given [`text::Parser`] and
256    /// [`text::Highlighter`].
257    pub fn highlight_with<P: text::Parser>(
258        self,
259        settings: P::Settings,
260        highlighter: impl text::Highlighter<P::Output, Theme> + 'a,
261    ) -> TextEditor<'a, P, Message, Theme, Renderer> {
262        TextEditor {
263            id: self.id,
264            content: self.content,
265            placeholder: self.placeholder,
266            font: self.font,
267            text_size: self.text_size,
268            line_height: self.line_height,
269            width: self.width,
270            height: self.height,
271            padding: self.padding,
272            wrapping: self.wrapping,
273            class: self.class,
274            key_binding: self.key_binding,
275            on_edit: self.on_edit,
276            parser_settings: settings,
277            highlighter: Some(Box::new(highlighter)),
278            last_status: self.last_status,
279        }
280    }
281
282    /// Sets the closure to produce key bindings on key presses.
283    ///
284    /// See [`Binding`] for the list of available bindings.
285    pub fn key_binding(
286        mut self,
287        key_binding: impl Fn(KeyPress) -> Option<Binding<Message>> + 'a,
288    ) -> Self {
289        self.key_binding = Some(Box::new(key_binding));
290        self
291    }
292
293    /// Sets the style of the [`TextEditor`].
294    #[must_use]
295    pub fn style(mut self, style: impl Fn(&Theme, Status) -> Style + 'a) -> Self
296    where
297        Theme::Class<'a>: From<StyleFn<'a, Theme>>,
298    {
299        self.class = (Box::new(style) as StyleFn<'a, Theme>).into();
300        self
301    }
302
303    /// Sets the style class of the [`TextEditor`].
304    #[cfg(feature = "advanced")]
305    #[must_use]
306    pub fn class(mut self, class: impl Into<Theme::Class<'a>>) -> Self {
307        self.class = class.into();
308        self
309    }
310}
311
312struct State<Parser: text::Parser> {
313    editor: editor::State,
314    parser: RefCell<Parser>,
315    parser_settings: Parser::Settings,
316    last_theme: RefCell<Option<String>>,
317}
318
319impl<Parser, Message, Theme, Renderer> widget::Meta
320    for TextEditor<'_, Parser, Message, Theme, Renderer>
321where
322    Parser: text::Parser,
323    Theme: Catalog,
324    Renderer: text::Renderer,
325{
326}
327
328impl<Parser, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
329    for TextEditor<'_, Parser, Message, Theme, Renderer>
330where
331    Parser: text::Parser,
332    Theme: Catalog,
333    Renderer: text::Renderer,
334{
335    fn tag(&self) -> widget::tree::Tag {
336        widget::tree::Tag::of::<State<Parser>>()
337    }
338
339    fn state(&self) -> widget::tree::State {
340        widget::tree::State::new(State {
341            editor: editor::State::new(),
342            parser: RefCell::new(Parser::new(&self.parser_settings)),
343            parser_settings: self.parser_settings.clone(),
344            last_theme: RefCell::new(None),
345        })
346    }
347
348    fn size(&self) -> Size<Length> {
349        Size {
350            width: self.width,
351            height: self.height,
352        }
353    }
354
355    fn layout(&mut self, tree: &mut widget::Tree, renderer: &Renderer, limits: &layout::Limits) {
356        let mut internal = self.content.0.borrow_mut();
357        let state = tree.state.downcast_mut::<State<Parser>>();
358
359        if state.parser_settings != self.parser_settings {
360            state.parser.borrow_mut().update(&self.parser_settings);
361
362            state.parser_settings = self.parser_settings.clone();
363        }
364
365        let limits = limits
366            .width(self.width)
367            .height(self.height)
368            .shrink(self.padding);
369
370        internal.editor.update(
371            limits.bounds(),
372            self.font.unwrap_or_else(|| renderer.font()),
373            self.text_size.unwrap_or_else(|| renderer.text_size()),
374            self.line_height.unwrap_or_else(|| renderer.line_height()),
375            self.wrapping,
376            text::Alignment::Default,
377            renderer.hint_factor(),
378            state.parser.borrow_mut().deref_mut(),
379        );
380
381        let bounds = limits.resolve(self.width, self.height, internal.editor.min_bounds());
382
383        tree.size = bounds.expand(self.padding);
384    }
385
386    fn update(
387        &mut self,
388        tree: &mut widget::Tree,
389        event: &Event,
390        layout: Layout,
391        cursor: mouse::Cursor,
392        _renderer: &Renderer,
393        shell: &mut Shell<'_, Message>,
394        _viewport: &Rectangle,
395    ) {
396        let Some(on_edit) = self.on_edit.as_ref() else {
397            return;
398        };
399
400        let state = tree.state.downcast_mut::<State<Parser>>();
401        let is_redraw = matches!(event, Event::Window(window::Event::RedrawRequested(_now)),);
402
403        let editor = &self.content.0.borrow().editor;
404
405        fn apply_update<Message>(
406            update: editor::Update<Message>,
407            shell: &mut Shell<'_, Message>,
408            on_edit: &impl Fn(editor::Action) -> Message,
409        ) {
410            match update {
411                editor::Update::Action(action) => {
412                    shell.publish(on_edit(action));
413                    shell.capture_event();
414                }
415                editor::Update::Release => {}
416                editor::Update::Custom(message) => {
417                    shell.publish(message);
418                }
419                editor::Update::Sequence(updates) => {
420                    for update in updates {
421                        apply_update(update, shell, on_edit);
422                    }
423                }
424                editor::Update::Copy(content) => {
425                    shell.write_clipboard(clipboard::Content::Text(content));
426                }
427                editor::Update::Paste => {
428                    shell.read_clipboard(clipboard::Kind::Text);
429                }
430                editor::Update::RedrawAt(at) => {
431                    shell.request_redraw_at(at);
432                }
433                editor::Update::Focus | editor::Update::Unfocus | editor::Update::InputMethod => {
434                    shell.request_redraw();
435                }
436            }
437        }
438
439        if let Some(update) = state.editor.update(
440            &self.content.0.borrow().editor,
441            event,
442            layout.bounds(),
443            self.padding,
444            cursor,
445            self.key_binding
446                .as_deref()
447                .unwrap_or(&Binding::from_key_press as _),
448        ) {
449            apply_update(update, shell, on_edit);
450        }
451
452        let status = {
453            let is_disabled = self.on_edit.is_none();
454            let is_hovered = cursor.is_over(layout.bounds());
455
456            if is_disabled {
457                Status::Disabled
458            } else if state.editor.is_focused() {
459                Status::Focused { is_hovered }
460            } else if is_hovered {
461                Status::Hovered
462            } else {
463                Status::Active
464            }
465        };
466
467        if is_redraw {
468            self.last_status = Some(status);
469
470            shell.request_input_method(
471                &state
472                    .editor
473                    .input_method(editor, layout.bounds().shrink(self.padding).position()),
474            );
475        } else if self
476            .last_status
477            .is_some_and(|last_status| status != last_status)
478        {
479            shell.request_redraw();
480        }
481    }
482
483    fn draw(
484        &self,
485        tree: &widget::Tree,
486        renderer: &mut Renderer,
487        theme: &Theme,
488        _defaults: &renderer::Style,
489        layout: Layout,
490        _cursor: mouse::Cursor,
491        viewport: &Rectangle,
492    ) {
493        let bounds = layout.bounds();
494
495        let mut internal = self.content.0.borrow_mut();
496        let state = tree.state.downcast_ref::<State<Parser>>();
497
498        let font = self.font.unwrap_or_else(|| renderer.font());
499
500        let theme_name = theme.name();
501
502        if state
503            .last_theme
504            .borrow()
505            .as_ref()
506            .is_none_or(|last_theme| last_theme != theme_name)
507        {
508            state.parser.borrow_mut().change_line(0);
509            let _ = state.last_theme.borrow_mut().replace(theme_name.to_owned());
510        }
511
512        internal
513            .editor
514            .highlight(font, state.parser.borrow_mut().deref_mut(), |output| {
515                let Some(highlighter) = &self.highlighter else {
516                    return highlighter::Style::default();
517                };
518
519                highlighter.highlight(output, theme)
520            });
521
522        let style = theme.style(&self.class, self.last_status.unwrap_or(Status::Active));
523
524        renderer.fill_quad(
525            renderer::Quad {
526                bounds,
527                border: style.border,
528                ..renderer::Quad::default()
529            },
530            style.background,
531        );
532
533        let text_bounds = bounds.shrink(self.padding);
534
535        if internal.editor.is_empty()
536            && let Some(placeholder) = &self.placeholder
537        {
538            renderer.fill_text(
539                Text {
540                    content: placeholder.clone().into_owned(),
541                    bounds: text_bounds.size(),
542                    size: self.text_size.unwrap_or_else(|| renderer.text_size()),
543                    line_height: self.line_height.unwrap_or_else(|| renderer.line_height()),
544                    font,
545                    align_x: text::Alignment::Default,
546                    align_y: alignment::Vertical::Top,
547                    shaping: text::Shaping::Advanced,
548                    wrapping: self.wrapping,
549                    ellipsis: text::Ellipsis::None,
550                    hint_factor: renderer.hint_factor(),
551                },
552                text_bounds.position(),
553                style.placeholder,
554                text_bounds,
555            );
556        }
557
558        state.editor.draw(
559            &internal.editor,
560            renderer,
561            text_bounds.position(),
562            *viewport,
563            editor::Style {
564                value: style.value,
565                selection: style.selection,
566            },
567        );
568    }
569
570    fn mouse_interaction(
571        &self,
572        _tree: &widget::Tree,
573        layout: Layout,
574        cursor: mouse::Cursor,
575        _viewport: &Rectangle,
576        _renderer: &Renderer,
577    ) -> mouse::Interaction {
578        let is_disabled = self.on_edit.is_none();
579
580        if cursor.is_over(layout.bounds()) {
581            if is_disabled {
582                mouse::Interaction::NotAllowed
583            } else {
584                mouse::Interaction::Text
585            }
586        } else {
587            mouse::Interaction::default()
588        }
589    }
590
591    fn operate(
592        &mut self,
593        tree: &mut widget::Tree,
594        layout: Layout,
595        _viewport: &Rectangle,
596        _renderer: &Renderer,
597        operation: &mut dyn widget::Operation,
598    ) {
599        let state = tree.state.downcast_mut::<State<Parser>>();
600
601        operation.focusable(self.id.as_ref(), layout.bounds(), &mut state.editor);
602        operation.text_input(
603            self.id.as_ref(),
604            layout.bounds(),
605            &mut self.content.0.borrow_mut().editor,
606        );
607    }
608}
609
610/// The content of a [`TextEditor`].
611pub struct Content<R = crate::Renderer>(RefCell<Internal<R>>)
612where
613    R: text::Renderer;
614
615struct Internal<R>
616where
617    R: text::Renderer,
618{
619    editor: R::Editor,
620}
621
622impl<R> Content<R>
623where
624    R: text::Renderer,
625{
626    /// Creates an empty [`Content`].
627    pub fn new() -> Self {
628        Self::with_text("")
629    }
630
631    /// Creates a [`Content`] with the given text.
632    pub fn with_text(text: &str) -> Self {
633        Self(RefCell::new(Internal {
634            editor: R::Editor::with_text(text),
635        }))
636    }
637
638    /// Performs an [`Action`] on the [`Content`].
639    pub fn perform(&mut self, action: Action) {
640        let internal = self.0.get_mut();
641
642        internal.editor.perform(action);
643    }
644
645    /// Moves the current cursor to reflect the given one.
646    pub fn move_to(&mut self, cursor: Cursor) {
647        let internal = self.0.get_mut();
648
649        internal.editor.move_to(cursor);
650    }
651
652    /// Returns the current cursor position of the [`Content`].
653    pub fn cursor(&self) -> Cursor {
654        self.0.borrow().editor.cursor()
655    }
656
657    /// Returns the amount of lines of the [`Content`].
658    pub fn line_count(&self) -> usize {
659        self.0.borrow().editor.line_count()
660    }
661
662    /// Returns the text of the line at the given index, if it exists.
663    pub fn line(&self, index: usize) -> Option<Line<'_>> {
664        let internal = self.0.borrow();
665        let line = internal.editor.line(index)?;
666
667        Some(Line {
668            text: Cow::Owned(line.text.into_owned()),
669            ending: line.ending,
670        })
671    }
672
673    /// Returns an iterator of the text of the lines in the [`Content`].
674    pub fn lines(&self) -> impl Iterator<Item = Line<'_>> {
675        (0..)
676            .map(|i| self.line(i))
677            .take_while(Option::is_some)
678            .flatten()
679    }
680
681    /// Returns the text of the [`Content`].
682    pub fn text(&self) -> String {
683        self.0.borrow().editor.text()
684    }
685
686    /// Returns the selected text of the [`Content`].
687    pub fn selection(&self) -> Option<String> {
688        self.0.borrow().editor.copy()
689    }
690
691    /// Returns the kind of [`LineEnding`] used for separating lines in the [`Content`].
692    pub fn line_ending(&self) -> Option<LineEnding> {
693        Some(self.line(0)?.ending)
694    }
695
696    /// Returns whether or not the the [`Content`] is empty.
697    pub fn is_empty(&self) -> bool {
698        self.0.borrow().editor.is_empty()
699    }
700}
701
702impl<Renderer> Clone for Content<Renderer>
703where
704    Renderer: text::Renderer,
705{
706    fn clone(&self) -> Self {
707        Self::with_text(&self.text())
708    }
709}
710
711impl<Renderer> Default for Content<Renderer>
712where
713    Renderer: text::Renderer,
714{
715    fn default() -> Self {
716        Self::new()
717    }
718}
719
720impl<Renderer> fmt::Debug for Content<Renderer>
721where
722    Renderer: text::Renderer,
723    Renderer::Editor: fmt::Debug,
724{
725    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
726        let internal = self.0.borrow();
727
728        f.debug_struct("Content")
729            .field("editor", &internal.editor)
730            .finish()
731    }
732}
733
734/// The possible status of a [`TextEditor`].
735#[derive(Debug, Clone, Copy, PartialEq, Eq)]
736pub enum Status {
737    /// The [`TextEditor`] can be interacted with.
738    Active,
739    /// The [`TextEditor`] is being hovered.
740    Hovered,
741    /// The [`TextEditor`] is focused.
742    Focused {
743        /// Whether the [`TextEditor`] is hovered, while focused.
744        is_hovered: bool,
745    },
746    /// The [`TextEditor`] cannot be interacted with.
747    Disabled,
748}
749
750/// The appearance of a text input.
751#[derive(Debug, Clone, Copy, PartialEq)]
752pub struct Style {
753    /// The [`Background`] of the text input.
754    pub background: Background,
755    /// The [`Border`] of the text input.
756    pub border: Border,
757    /// The [`Color`] of the placeholder of the text input.
758    pub placeholder: Color,
759    /// The [`Color`] of the value of the text input.
760    pub value: Color,
761    /// The [`Color`] of the selection of the text input.
762    pub selection: Color,
763}
764
765/// The theme catalog of a [`TextEditor`].
766pub trait Catalog: theme::Base {
767    /// The item class of the [`Catalog`].
768    type Class<'a>;
769
770    /// The default class produced by the [`Catalog`].
771    fn default<'a>() -> Self::Class<'a>;
772
773    /// The [`Style`] of a class with the given status.
774    fn style(&self, class: &Self::Class<'_>, status: Status) -> Style;
775}
776
777/// A styling function for a [`TextEditor`].
778pub type StyleFn<'a, Theme> = Box<dyn Fn(&Theme, Status) -> Style + 'a>;
779
780impl Catalog for Theme {
781    type Class<'a> = StyleFn<'a, Self>;
782
783    fn default<'a>() -> Self::Class<'a> {
784        Box::new(default)
785    }
786
787    fn style(&self, class: &Self::Class<'_>, status: Status) -> Style {
788        class(self, status)
789    }
790}
791
792/// The default style of a [`TextEditor`].
793pub fn default(theme: &Theme, status: Status) -> Style {
794    let palette = theme.palette();
795
796    let active = Style {
797        background: Background::Color(palette.background.weakest.color),
798        border: Border {
799            radius: 2.0.into(),
800            width: 1.0,
801            color: palette.background.strong.color,
802        },
803        placeholder: palette.secondary.base.color,
804        value: palette.background.weakest.text,
805        selection: palette.background.strongest.color,
806    };
807
808    match status {
809        Status::Active => active,
810        Status::Hovered => Style {
811            border: Border {
812                color: palette.background.base.text,
813                ..active.border
814            },
815            ..active
816        },
817        Status::Focused { .. } => Style {
818            border: Border {
819                color: palette.primary.strong.color,
820                ..active.border
821            },
822            ..active
823        },
824        Status::Disabled => Style {
825            background: Background::Color(palette.background.base.color),
826            value: active.placeholder,
827            placeholder: palette.background.strongest.color,
828            ..active
829        },
830    }
831}