Skip to main content

gpui_kit_grid/state/editors/
text.rs

1//! `TextEditor`: a single-line text field.
2
3use gpui_kit::{App, SharedString, Window};
4use rust_i18n::t;
5
6use super::{InputField, initial_text};
7use crate::{
8    model::{GridRow, OwnedValue},
9    state::edit::{CellEditor, EditError, EditorOpen, EditorStart},
10};
11
12/// Edits a cell as one line of text, on gpui-kit's `Input`. Values: `Text`;
13/// an empty draft is [`Self::with_empty_value`]'s value, `Empty` by default:
14/// an empty cell has no value, as in Excel.
15///
16/// F2 starts from the cell's text with the caret at its end, Backspace and
17/// Delete from an empty field, typing from the typed text.
18#[derive(Clone, Debug)]
19pub struct TextEditor {
20    placeholder: Option<SharedString>,
21    max_length: Option<usize>,
22    empty_value: OwnedValue,
23}
24
25impl Default for TextEditor {
26    fn default() -> Self {
27        Self::new()
28    }
29}
30
31impl TextEditor {
32    /// A text editor without a placeholder or a length limit.
33    pub fn new() -> Self {
34        Self {
35            placeholder: None,
36            max_length: None,
37            empty_value: OwnedValue::Empty,
38        }
39    }
40
41    /// Sets the text an empty field shows.
42    pub fn with_placeholder(mut self, text: impl Into<SharedString>) -> Self {
43        self.placeholder = Some(text.into());
44        self
45    }
46
47    /// Refuses a draft longer than `chars` characters.
48    pub fn with_max_length(mut self, chars: usize) -> Self {
49        self.max_length = Some(chars);
50        self
51    }
52
53    /// Sets what an empty draft commits (default `Empty`), such as
54    /// `OwnedValue::Text("".into())`.
55    pub fn with_empty_value(mut self, value: OwnedValue) -> Self {
56        self.empty_value = value;
57        self
58    }
59
60    /// The text an empty field shows.
61    pub fn placeholder(&self) -> Option<&SharedString> {
62        self.placeholder.as_ref()
63    }
64
65    /// The longest draft, in characters.
66    pub fn max_length(&self) -> Option<usize> {
67        self.max_length
68    }
69
70    /// What an empty draft commits.
71    pub fn empty_value(&self) -> &OwnedValue {
72        &self.empty_value
73    }
74
75    /// Parses a draft.
76    fn parse(
77        draft: &str,
78        max_length: Option<usize>,
79        empty: &OwnedValue,
80    ) -> Result<OwnedValue, EditError> {
81        if draft.is_empty() {
82            return Ok(empty.clone());
83        }
84        if let Some(max) = max_length
85            && draft.chars().count() > max
86        {
87            return Err(EditError::new(t!("live_grid.edit.too_long", count = max)));
88        }
89        Ok(OwnedValue::Text(draft.into()))
90    }
91}
92
93impl<R: GridRow> CellEditor<R> for TextEditor {
94    fn open(&self, start: &EditorStart<'_, R>, window: &mut Window, cx: &mut App) -> EditorOpen {
95        let text = initial_text(start, '.');
96        let placeholder = self.placeholder.clone();
97        let state = InputField::input_state(
98            &text,
99            |state| match placeholder {
100                Some(placeholder) => state.placeholder(placeholder),
101                None => state,
102            },
103            window,
104            cx,
105        );
106        let (max_length, empty) = (self.max_length, self.empty_value.clone());
107        EditorOpen::Field(Box::new(InputField::new(
108            state,
109            text,
110            false,
111            Box::new(move |draft| Self::parse(draft, max_length, &empty)),
112        )))
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use super::*;
119
120    #[test]
121    fn test_text_editor_parse() {
122        let parse = |draft: &str, max: Option<usize>, empty: &OwnedValue| {
123            TextEditor::parse(draft, max, empty)
124        };
125        assert_eq!(parse("", None, &OwnedValue::Empty), Ok(OwnedValue::Empty));
126        let blank = OwnedValue::Text("".into());
127        assert_eq!(parse("", None, &blank), Ok(blank.clone()));
128        assert_eq!(
129            parse("Сбер", Some(4), &OwnedValue::Empty),
130            Ok(OwnedValue::Text("Сбер".into()))
131        );
132        let error = parse("Сбербанк", Some(4), &OwnedValue::Empty).unwrap_err();
133        assert_eq!(error.message().as_ref(), "Use at most 4 characters");
134        let editor = TextEditor::new()
135            .with_placeholder("Note")
136            .with_max_length(10)
137            .with_empty_value(blank.clone());
138        assert_eq!(editor.placeholder().map(AsRef::as_ref), Some("Note"));
139        assert_eq!(editor.max_length(), Some(10));
140        assert_eq!(editor.empty_value(), &blank);
141    }
142}