kas_core/text/
selection.rs1use crate::geom::{Rect, Vec2};
9use crate::theme::Text;
10use cast::CastFloat;
11use kas_text::{TextDisplay, format::FormattableText};
12use std::ops::Range;
13use unicode_segmentation::UnicodeSegmentation;
14
15#[derive(Default)]
17pub struct SelectionAction {
18 pub anchor: bool,
19 pub clear: bool,
20 pub repeats: u32,
21}
22
23impl SelectionAction {
24 pub fn new(anchor: bool, clear: bool, repeats: u32) -> Self {
26 SelectionAction {
27 anchor,
28 clear,
29 repeats,
30 }
31 }
32}
33
34#[derive(Clone, Debug, Default)]
45pub struct SelectionHelper {
46 edit: usize,
47 sel: usize,
48 anchor: usize,
49}
50
51impl SelectionHelper {
52 pub fn new(edit: usize, selection: usize) -> Self {
56 SelectionHelper {
57 edit,
58 sel: selection,
59 anchor: selection,
60 }
61 }
62
63 pub fn clear(&mut self) {
67 *self = Self::default();
68 }
69
70 pub fn is_empty(&self) -> bool {
72 self.edit == self.sel
73 }
74 pub fn set_empty(&mut self) {
76 self.sel = self.edit;
77 self.anchor = self.edit;
78 }
79
80 pub fn set_all(&mut self, index: usize) {
82 self.edit = index;
83 self.sel = index;
84 self.anchor = index;
85 }
86
87 pub fn edit_index(&self) -> usize {
89 self.edit
90 }
91 pub fn set_edit_index(&mut self, index: usize) {
93 self.edit = index;
94 }
95
96 pub fn sel_index(&self) -> usize {
98 self.sel
99 }
100 pub fn set_sel_index(&mut self, index: usize) {
104 self.sel = index;
105 self.anchor = index;
106 }
107 pub fn set_sel_index_only(&mut self, index: usize) {
111 self.sel = index;
112 }
113
114 pub fn set_max_len(&mut self, len: usize) {
119 self.edit = self.edit.min(len);
120 self.sel = self.sel.min(len);
121 self.anchor = self.anchor.min(len);
122 }
123
124 pub fn range(&self) -> Range<usize> {
129 let mut range = self.edit..self.sel;
130 if range.start > range.end {
131 std::mem::swap(&mut range.start, &mut range.end);
132 }
133 range
134 }
135
136 pub fn set_anchor_to_range_start(&mut self) {
138 self.anchor = self.range().start;
139 }
140
141 pub fn anchor_to_edit_range(&self) -> Range<usize> {
146 debug_assert!(self.anchor <= self.edit);
147 self.anchor..self.edit
148 }
149
150 fn expand<T: FormattableText>(&mut self, text: &Text<T>, repeats: u32) {
160 let string = text.as_str();
161 let mut range = self.edit..self.anchor;
162 if range.start > range.end {
163 std::mem::swap(&mut range.start, &mut range.end);
164 }
165 let (mut start, mut end);
166 if repeats <= 2 {
167 end = string[range.start..]
168 .char_indices()
169 .nth(1)
170 .map(|(i, _)| range.start + i)
171 .unwrap_or(string.len());
172 start = string[0..end]
173 .split_word_bound_indices()
174 .next_back()
175 .map(|(index, _)| index)
176 .unwrap_or(0);
177 end = string[start..]
178 .split_word_bound_indices()
179 .find_map(|(index, _)| {
180 let pos = start + index;
181 (pos >= range.end).then_some(pos)
182 })
183 .unwrap_or(string.len());
184 } else {
185 start = match text.find_line(range.start) {
186 Ok(Some(r)) => r.1.start,
187 _ => 0,
188 };
189 end = match text.find_line(range.end) {
190 Ok(Some(r)) => r.1.end,
191 _ => string.len(),
192 };
193 }
194
195 if self.edit < self.sel {
196 std::mem::swap(&mut start, &mut end);
197 }
198 self.sel = start;
199 self.edit = end;
200 }
201
202 pub fn action<T: FormattableText>(&mut self, text: &Text<T>, action: SelectionAction) {
204 if action.anchor {
205 self.anchor = self.edit;
206 }
207 if action.clear {
208 self.set_empty();
209 }
210 if action.repeats > 1 {
211 self.expand(text, action.repeats);
212 }
213 }
214
215 pub fn cursor_rect(&self, text: &TextDisplay) -> Option<Rect> {
217 let (m1, m2);
218 if self.sel == self.edit {
219 let mut iter = text.text_glyph_pos(self.edit);
220 m1 = iter.next();
221 m2 = iter.next();
222 } else if self.sel < self.edit {
223 m1 = text.text_glyph_pos(self.sel).next_back();
224 m2 = text.text_glyph_pos(self.edit).next();
225 } else {
226 m1 = text.text_glyph_pos(self.edit).next_back();
227 m2 = text.text_glyph_pos(self.sel).next();
228 }
229
230 if let Some((c1, c2)) = m1.zip(m2) {
231 let left = c1.pos.0.min(c2.pos.0);
232 let right = c1.pos.0.max(c2.pos.0);
233 let top = (c1.pos.1 - c1.ascent).min(c2.pos.1 - c2.ascent);
234 let bottom = (c1.pos.1 - c1.descent).max(c2.pos.1 - c2.ascent);
235 let p1 = Vec2(left, top).cast_floor();
236 let p2 = Vec2(right, bottom).cast_ceil();
237 Some(Rect::from_coords(p1, p2))
238 } else if let Some(c) = m1.or(m2) {
239 let p1 = Vec2(c.pos.0, c.pos.1 - c.ascent).cast_floor();
240 let p2 = Vec2(c.pos.0, c.pos.1 - c.descent).cast_ceil();
241 Some(Rect::from_coords(p1, p2))
242 } else {
243 None
244 }
245 }
246}