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                    shell.capture_event();
419                }
420                editor::Update::Sequence(updates) => {
421                    for update in updates {
422                        apply_update(update, shell, on_edit);
423                    }
424                }
425                editor::Update::Copy(content) => {
426                    shell.write_clipboard(clipboard::Content::Text(content));
427                    shell.capture_event();
428                }
429                editor::Update::Paste => {
430                    shell.read_clipboard(clipboard::Kind::Text);
431                    shell.capture_event();
432                }
433                editor::Update::RedrawAt(at) => {
434                    shell.request_redraw_at(at);
435                }
436                editor::Update::Focus | editor::Update::Unfocus | editor::Update::InputMethod => {
437                    shell.request_redraw();
438                }
439            }
440        }
441
442        if let Some(update) = state.editor.update(
443            &self.content.0.borrow().editor,
444            event,
445            layout.bounds(),
446            self.padding,
447            cursor,
448            self.key_binding
449                .as_deref()
450                .unwrap_or(&Binding::from_key_press as _),
451        ) {
452            apply_update(update, shell, on_edit);
453        }
454
455        let status = {
456            let is_disabled = self.on_edit.is_none();
457            let is_hovered = cursor.is_over(layout.bounds());
458
459            if is_disabled {
460                Status::Disabled
461            } else if state.editor.is_focused() {
462                Status::Focused { is_hovered }
463            } else if is_hovered {
464                Status::Hovered
465            } else {
466                Status::Active
467            }
468        };
469
470        if is_redraw {
471            self.last_status = Some(status);
472
473            shell.request_input_method(
474                &state
475                    .editor
476                    .input_method(editor, layout.bounds().shrink(self.padding).position()),
477            );
478        } else if self
479            .last_status
480            .is_some_and(|last_status| status != last_status)
481        {
482            shell.request_redraw();
483        }
484    }
485
486    fn draw(
487        &self,
488        tree: &widget::Tree,
489        renderer: &mut Renderer,
490        theme: &Theme,
491        _defaults: &renderer::Style,
492        layout: Layout,
493        _cursor: mouse::Cursor,
494        viewport: &Rectangle,
495    ) {
496        let bounds = layout.bounds();
497
498        let mut internal = self.content.0.borrow_mut();
499        let state = tree.state.downcast_ref::<State<Parser>>();
500
501        let font = self.font.unwrap_or_else(|| renderer.font());
502
503        let theme_name = theme.name();
504
505        if state
506            .last_theme
507            .borrow()
508            .as_ref()
509            .is_none_or(|last_theme| last_theme != theme_name)
510        {
511            state.parser.borrow_mut().change_line(0);
512            let _ = state.last_theme.borrow_mut().replace(theme_name.to_owned());
513        }
514
515        internal
516            .editor
517            .highlight(font, state.parser.borrow_mut().deref_mut(), |output| {
518                let Some(highlighter) = &self.highlighter else {
519                    return highlighter::Style::default();
520                };
521
522                highlighter.highlight(output, theme)
523            });
524
525        let style = theme.style(&self.class, self.last_status.unwrap_or(Status::Active));
526
527        renderer.fill_quad(
528            renderer::Quad {
529                bounds,
530                border: style.border,
531                ..renderer::Quad::default()
532            },
533            style.background,
534        );
535
536        let text_bounds = bounds.shrink(self.padding);
537
538        if internal.editor.is_empty()
539            && let Some(placeholder) = &self.placeholder
540        {
541            renderer.fill_text(
542                Text {
543                    content: placeholder.clone().into_owned(),
544                    bounds: text_bounds.size(),
545                    size: self.text_size.unwrap_or_else(|| renderer.text_size()),
546                    line_height: self.line_height.unwrap_or_else(|| renderer.line_height()),
547                    font,
548                    align_x: text::Alignment::Default,
549                    align_y: alignment::Vertical::Top,
550                    shaping: text::Shaping::Advanced,
551                    wrapping: self.wrapping,
552                    ellipsis: text::Ellipsis::None,
553                    hint_factor: renderer.hint_factor(),
554                },
555                text_bounds.position(),
556                style.placeholder,
557                text_bounds,
558            );
559        }
560
561        state.editor.draw(
562            &internal.editor,
563            renderer,
564            text_bounds.position(),
565            *viewport,
566            editor::Style {
567                value: style.value,
568                selection: style.selection,
569            },
570        );
571    }
572
573    fn mouse_interaction(
574        &self,
575        _tree: &widget::Tree,
576        layout: Layout,
577        cursor: mouse::Cursor,
578        _viewport: &Rectangle,
579        _renderer: &Renderer,
580    ) -> mouse::Interaction {
581        let is_disabled = self.on_edit.is_none();
582
583        if cursor.is_over(layout.bounds()) {
584            if is_disabled {
585                mouse::Interaction::NotAllowed
586            } else {
587                mouse::Interaction::Text
588            }
589        } else {
590            mouse::Interaction::default()
591        }
592    }
593
594    fn operate(
595        &mut self,
596        tree: &mut widget::Tree,
597        layout: Layout,
598        _viewport: &Rectangle,
599        _renderer: &Renderer,
600        operation: &mut dyn widget::Operation,
601    ) {
602        let state = tree.state.downcast_mut::<State<Parser>>();
603
604        operation.focusable(self.id.as_ref(), layout.bounds(), &mut state.editor);
605        operation.text_input(
606            self.id.as_ref(),
607            layout.bounds(),
608            &mut self.content.0.borrow_mut().editor,
609        );
610    }
611}
612
613/// The content of a [`TextEditor`].
614pub struct Content<R = crate::Renderer>(RefCell<Internal<R>>)
615where
616    R: text::Renderer;
617
618struct Internal<R>
619where
620    R: text::Renderer,
621{
622    editor: R::Editor,
623}
624
625impl<R> Content<R>
626where
627    R: text::Renderer,
628{
629    /// Creates an empty [`Content`].
630    pub fn new() -> Self {
631        Self::with_text("")
632    }
633
634    /// Creates a [`Content`] with the given text.
635    pub fn with_text(text: &str) -> Self {
636        Self(RefCell::new(Internal {
637            editor: R::Editor::with_text(text),
638        }))
639    }
640
641    /// Performs an [`Action`] on the [`Content`].
642    pub fn perform(&mut self, action: Action) {
643        let internal = self.0.get_mut();
644
645        internal.editor.perform(action);
646    }
647
648    /// Moves the current cursor to reflect the given one.
649    pub fn move_to(&mut self, cursor: Cursor) {
650        let internal = self.0.get_mut();
651
652        internal.editor.move_to(cursor);
653    }
654
655    /// Returns the current cursor position of the [`Content`].
656    pub fn cursor(&self) -> Cursor {
657        self.0.borrow().editor.cursor()
658    }
659
660    /// Returns the amount of lines of the [`Content`].
661    pub fn line_count(&self) -> usize {
662        self.0.borrow().editor.line_count()
663    }
664
665    /// Returns the text of the line at the given index, if it exists.
666    pub fn line(&self, index: usize) -> Option<Line<'_>> {
667        let internal = self.0.borrow();
668        let line = internal.editor.line(index)?;
669
670        Some(Line {
671            text: Cow::Owned(line.text.into_owned()),
672            ending: line.ending,
673        })
674    }
675
676    /// Returns an iterator of the text of the lines in the [`Content`].
677    pub fn lines(&self) -> impl Iterator<Item = Line<'_>> {
678        (0..)
679            .map(|i| self.line(i))
680            .take_while(Option::is_some)
681            .flatten()
682    }
683
684    /// Returns the text of the [`Content`].
685    pub fn text(&self) -> String {
686        self.0.borrow().editor.text()
687    }
688
689    /// Returns the selected text of the [`Content`].
690    pub fn selection(&self) -> Option<String> {
691        self.0.borrow().editor.copy()
692    }
693
694    /// Returns the kind of [`LineEnding`] used for separating lines in the [`Content`].
695    pub fn line_ending(&self) -> Option<LineEnding> {
696        Some(self.line(0)?.ending)
697    }
698
699    /// Returns whether or not the the [`Content`] is empty.
700    pub fn is_empty(&self) -> bool {
701        self.0.borrow().editor.is_empty()
702    }
703}
704
705impl<Renderer> Clone for Content<Renderer>
706where
707    Renderer: text::Renderer,
708{
709    fn clone(&self) -> Self {
710        Self::with_text(&self.text())
711    }
712}
713
714impl<Renderer> Default for Content<Renderer>
715where
716    Renderer: text::Renderer,
717{
718    fn default() -> Self {
719        Self::new()
720    }
721}
722
723impl<Renderer> fmt::Debug for Content<Renderer>
724where
725    Renderer: text::Renderer,
726    Renderer::Editor: fmt::Debug,
727{
728    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
729        let internal = self.0.borrow();
730
731        f.debug_struct("Content")
732            .field("editor", &internal.editor)
733            .finish()
734    }
735}
736
737/// The possible status of a [`TextEditor`].
738#[derive(Debug, Clone, Copy, PartialEq, Eq)]
739pub enum Status {
740    /// The [`TextEditor`] can be interacted with.
741    Active,
742    /// The [`TextEditor`] is being hovered.
743    Hovered,
744    /// The [`TextEditor`] is focused.
745    Focused {
746        /// Whether the [`TextEditor`] is hovered, while focused.
747        is_hovered: bool,
748    },
749    /// The [`TextEditor`] cannot be interacted with.
750    Disabled,
751}
752
753/// The appearance of a text input.
754#[derive(Debug, Clone, Copy, PartialEq)]
755pub struct Style {
756    /// The [`Background`] of the text input.
757    pub background: Background,
758    /// The [`Border`] of the text input.
759    pub border: Border,
760    /// The [`Color`] of the placeholder of the text input.
761    pub placeholder: Color,
762    /// The [`Color`] of the value of the text input.
763    pub value: Color,
764    /// The [`Color`] of the selection of the text input.
765    pub selection: Color,
766}
767
768/// The theme catalog of a [`TextEditor`].
769pub trait Catalog: theme::Base {
770    /// The item class of the [`Catalog`].
771    type Class<'a>;
772
773    /// The default class produced by the [`Catalog`].
774    fn default<'a>() -> Self::Class<'a>;
775
776    /// The [`Style`] of a class with the given status.
777    fn style(&self, class: &Self::Class<'_>, status: Status) -> Style;
778}
779
780/// A styling function for a [`TextEditor`].
781pub type StyleFn<'a, Theme> = Box<dyn Fn(&Theme, Status) -> Style + 'a>;
782
783impl Catalog for Theme {
784    type Class<'a> = StyleFn<'a, Self>;
785
786    fn default<'a>() -> Self::Class<'a> {
787        Box::new(default)
788    }
789
790    fn style(&self, class: &Self::Class<'_>, status: Status) -> Style {
791        class(self, status)
792    }
793}
794
795/// The default style of a [`TextEditor`].
796pub fn default(theme: &Theme, status: Status) -> Style {
797    let palette = theme.palette();
798
799    let active = Style {
800        background: Background::Color(palette.background.weakest.color),
801        border: Border {
802            radius: 2.0.into(),
803            width: 1.0,
804            color: palette.background.strong.color,
805        },
806        placeholder: palette.secondary.base.color,
807        value: palette.background.weakest.text,
808        selection: palette.background.strongest.color,
809    };
810
811    match status {
812        Status::Active => active,
813        Status::Hovered => Style {
814            border: Border {
815                color: palette.background.base.text,
816                ..active.border
817            },
818            ..active
819        },
820        Status::Focused { .. } => Style {
821            border: Border {
822                color: palette.primary.strong.color,
823                ..active.border
824            },
825            ..active
826        },
827        Status::Disabled => Style {
828            background: Background::Color(palette.background.base.color),
829            value: active.placeholder,
830            placeholder: palette.background.strongest.color,
831            ..active
832        },
833    }
834}