cosmic/widget/text_input/
value.rs

1// Copyright 2019 H�ctor Ram�n, Iced contributors
2// Copyright 2023 System76 <info@system76.com>
3// SPDX-License-Identifier: MIT
4
5use unicode_segmentation::UnicodeSegmentation;
6
7/// The value of a [`TextInput`].
8///
9/// [`TextInput`]: crate::widget::TextInput
10// TODO: Reduce allocations, cache results (?)
11#[derive(Default, Debug, Clone, PartialEq)]
12pub struct Value {
13    graphemes: Vec<String>,
14}
15
16impl Value {
17    /// Creates a new [`Value`] from a string slice.
18    pub fn new(string: &str) -> Self {
19        let graphemes = UnicodeSegmentation::graphemes(string, true)
20            .map(String::from)
21            .collect();
22
23        Self { graphemes }
24    }
25
26    /// Returns whether the [`Value`] is empty or not.
27    ///
28    /// A [`Value`] is empty when it contains no graphemes.
29    #[must_use]
30    #[inline]
31    pub fn is_empty(&self) -> bool {
32        self.len() == 0
33    }
34
35    /// Returns the total amount of graphemes in the [`Value`].
36    #[must_use]
37    #[inline]
38    pub fn len(&self) -> usize {
39        self.graphemes.len()
40    }
41
42    /// Returns the position of the previous start of a word from the given
43    /// grapheme `index`.
44    #[must_use]
45    pub fn previous_start_of_word(&self, index: usize) -> usize {
46        let previous_string = &self.graphemes[..index.min(self.graphemes.len())].concat();
47
48        UnicodeSegmentation::split_word_bound_indices(previous_string as &str)
49            .filter(|(_, word)| !word.trim_start().is_empty())
50            .next_back()
51            .map_or(0, |(i, previous_word)| {
52                index
53                    - UnicodeSegmentation::graphemes(previous_word, true).count()
54                    - UnicodeSegmentation::graphemes(
55                        &previous_string[i + previous_word.len()..] as &str,
56                        true,
57                    )
58                    .count()
59            })
60    }
61
62    /// Returns the position of the next end of a word from the given grapheme
63    /// `index`.
64    #[must_use]
65    pub fn next_end_of_word(&self, index: usize) -> usize {
66        let next_string = &self.graphemes[index..].concat();
67
68        UnicodeSegmentation::split_word_bound_indices(next_string as &str)
69            .find(|(_, word)| !word.trim_start().is_empty())
70            .map_or(self.len(), |(i, next_word)| {
71                index
72                    + UnicodeSegmentation::graphemes(next_word, true).count()
73                    + UnicodeSegmentation::graphemes(&next_string[..i] as &str, true).count()
74            })
75    }
76
77    /// Returns a new [`Value`] containing the graphemes from `start` until the
78    /// given `end`.
79    #[must_use]
80    #[inline]
81    pub fn select(&self, start: usize, end: usize) -> Self {
82        let graphemes = self.graphemes[start.min(self.len())..end.min(self.len())].to_vec();
83
84        Self { graphemes }
85    }
86
87    /// Returns a new [`Value`] containing the graphemes until the given
88    /// `index`.
89    #[must_use]
90    #[inline]
91    pub fn until(&self, index: usize) -> Self {
92        let graphemes = self.graphemes[..index.min(self.len())].to_vec();
93
94        Self { graphemes }
95    }
96
97    /// Inserts a new `char` at the given grapheme `index`.
98    #[inline]
99    pub fn insert(&mut self, index: usize, c: char) {
100        self.graphemes.insert(index, c.to_string());
101
102        self.graphemes = UnicodeSegmentation::graphemes(&self.to_string() as &str, true)
103            .map(String::from)
104            .collect();
105    }
106
107    /// Inserts a bunch of graphemes at the given grapheme `index`.
108    #[inline]
109    pub fn insert_many(&mut self, index: usize, mut value: Value) {
110        let _ = self
111            .graphemes
112            .splice(index..index, value.graphemes.drain(..));
113    }
114
115    /// Removes the grapheme at the given `index`.
116    #[inline]
117    pub fn remove(&mut self, index: usize) {
118        let _ = self.graphemes.remove(index);
119    }
120
121    /// Removes the graphemes from `start` to `end`.
122    #[inline]
123    pub fn remove_many(&mut self, start: usize, end: usize) {
124        let _ = self.graphemes.splice(start..end, std::iter::empty());
125    }
126
127    /// Returns a new [`Value`] with all its graphemes replaced with the
128    /// dot ('•') character.
129    #[must_use]
130    pub fn secure(&self) -> Self {
131        Self {
132            graphemes: std::iter::repeat(String::from("•"))
133                .take(self.graphemes.len())
134                .collect(),
135        }
136    }
137}
138
139impl ToString for Value {
140    #[inline]
141    fn to_string(&self) -> String {
142        self.graphemes.concat()
143    }
144}