cosmic/widget/text_input/
cursor.rs1use iced_core::text::Affinity;
7
8use super::value::Value;
9
10#[derive(Debug, Copy, Clone, PartialEq, Eq)]
12pub struct Cursor {
13 state: State,
14 affinity: Affinity,
15}
16
17#[derive(Debug, Copy, Clone, PartialEq, Eq)]
19pub enum State {
20 Index(usize),
22
23 Selection {
25 start: usize,
27 end: usize,
29 },
30}
31
32impl Default for Cursor {
33 #[inline]
34 fn default() -> Self {
35 Self {
36 state: State::Index(0),
37 affinity: Affinity::Before,
38 }
39 }
40}
41
42impl Cursor {
43 #[must_use]
45 #[inline(never)]
46 pub fn state(&self, value: &Value) -> State {
47 match self.state {
48 State::Index(index) => State::Index(index.min(value.len())),
49 State::Selection { start, end } => {
50 let start = start.min(value.len());
51 let end = end.min(value.len());
52
53 if start == end {
54 State::Index(start)
55 } else {
56 State::Selection { start, end }
57 }
58 }
59 }
60 }
61
62 #[must_use]
66 #[inline]
67 pub fn selection(&self, value: &Value) -> Option<(usize, usize)> {
68 match self.state(value) {
69 State::Selection { start, end } => Some((start.min(end), start.max(end))),
70 State::Index(_) => None,
71 }
72 }
73
74 #[inline]
75 pub(crate) fn move_to(&mut self, position: usize) {
76 self.state = State::Index(position);
77 }
78
79 pub(crate) fn clear_selection(&mut self) {
80 if let State::Selection { end, .. } = self.state {
81 self.state = State::Index(end);
82 }
83 }
84
85 #[inline]
86 pub(crate) fn move_right(&mut self, value: &Value) {
87 self.move_right_by_amount(value, 1);
88 }
89
90 #[inline]
91 pub(crate) fn move_right_by_words(&mut self, value: &Value) {
92 self.move_to(value.next_end_of_word(self.right(value)));
93 }
94
95 #[inline]
96 pub(crate) fn move_right_by_amount(&mut self, value: &Value, amount: usize) {
97 match self.state(value) {
98 State::Index(index) => self.move_to(index.saturating_add(amount).min(value.len())),
99 State::Selection { start, end } => self.move_to(end.max(start)),
100 }
101 }
102
103 #[inline]
104 pub(crate) fn move_left(&mut self, value: &Value) {
105 match self.state(value) {
106 State::Index(index) if index > 0 => self.move_to(index - 1),
107 State::Selection { start, end } => self.move_to(start.min(end)),
108 State::Index(_) => self.move_to(0),
109 }
110 }
111
112 #[inline]
113 pub(crate) fn move_left_by_words(&mut self, value: &Value) {
114 self.move_to(value.previous_start_of_word(self.left(value)));
115 }
116
117 #[inline]
118 pub(crate) fn select_range(&mut self, start: usize, end: usize) {
119 self.state = if start == end {
120 State::Index(start)
121 } else {
122 State::Selection { start, end }
123 };
124 }
125
126 #[inline]
127 pub(crate) fn select_left(&mut self, value: &Value) {
128 match self.state(value) {
129 State::Index(index) if index > 0 => self.select_range(index, index - 1),
130 State::Selection { start, end } if end > 0 => self.select_range(start, end - 1),
131 _ => {}
132 }
133 }
134
135 #[inline]
136 pub(crate) fn select_right(&mut self, value: &Value) {
137 match self.state(value) {
138 State::Index(index) if index < value.len() => self.select_range(index, index + 1),
139 State::Selection { start, end } if end < value.len() => {
140 self.select_range(start, end + 1);
141 }
142 _ => {}
143 }
144 }
145
146 #[inline]
147 pub(crate) fn select_left_by_words(&mut self, value: &Value) {
148 match self.state(value) {
149 State::Index(index) => self.select_range(index, value.previous_start_of_word(index)),
150 State::Selection { start, end } => {
151 self.select_range(start, value.previous_start_of_word(end));
152 }
153 }
154 }
155
156 #[inline]
157 pub(crate) fn select_right_by_words(&mut self, value: &Value) {
158 match self.state(value) {
159 State::Index(index) => self.select_range(index, value.next_end_of_word(index)),
160 State::Selection { start, end } => {
161 self.select_range(start, value.next_end_of_word(end));
162 }
163 }
164 }
165
166 #[inline]
167 pub(crate) fn select_all(&mut self, value: &Value) {
168 self.select_range(0, value.len());
169 }
170
171 #[inline]
172 pub(crate) fn start(&self, value: &Value) -> usize {
173 let start = match self.state {
174 State::Index(index) => index,
175 State::Selection { start, .. } => start,
176 };
177
178 start.min(value.len())
179 }
180
181 #[inline]
182 pub(crate) fn end(&self, value: &Value) -> usize {
183 let end = match self.state {
184 State::Index(index) => index,
185 State::Selection { end, .. } => end,
186 };
187
188 end.min(value.len())
189 }
190
191 #[inline]
192 fn left(&self, value: &Value) -> usize {
193 match self.state(value) {
194 State::Index(index) => index,
195 State::Selection { start, end } => start.min(end),
196 }
197 }
198
199 #[inline]
200 fn right(&self, value: &Value) -> usize {
201 match self.state(value) {
202 State::Index(index) => index,
203 State::Selection { start, end } => start.max(end),
204 }
205 }
206
207 #[must_use]
209 pub fn affinity(&self) -> Affinity {
210 self.affinity
211 }
212
213 pub fn set_affinity(&mut self, affinity: Affinity) {
215 self.affinity = affinity;
216 }
217
218 pub fn move_visual(&mut self, forward: bool, by_words: bool, rtl: bool, value: &Value) {
222 match (forward ^ rtl, by_words) {
223 (true, false) => self.move_right(value),
224 (true, true) => self.move_right_by_words(value),
225 (false, false) => self.move_left(value),
226 (false, true) => self.move_left_by_words(value),
227 }
228 }
229
230 pub fn select_visual(&mut self, forward: bool, by_words: bool, rtl: bool, value: &Value) {
232 match (forward ^ rtl, by_words) {
233 (true, false) => self.select_right(value),
234 (true, true) => self.select_right_by_words(value),
235 (false, false) => self.select_left(value),
236 (false, true) => self.select_left_by_words(value),
237 }
238 }
239}