1use std::cell::RefCell;
5use std::ops;
6use std::rc::Rc;
7use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
8
9use cosmic_text::{Affinity, AttrsList, Cursor, Metrics};
10use smallvec::SmallVec;
11
12use crate::BASE_DPI;
13use crate::graphics::point_to_pixel;
14
15#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct Change {
21 pub start: Cursor,
22 pub end: Cursor,
23 pub old: SmallVec<[u8; 4]>,
24 pub attrs: Option<AttrsList>,
25}
26
27#[derive(Debug)]
28pub struct EditBuffer {
29 pub(crate) buffer: Rc<RefCell<cosmic_text::Buffer>>,
30 pub(crate) count: AtomicUsize,
31 pub(crate) reflow: AtomicBool,
32 cursor: AtomicUsize,
33 select: AtomicUsize, dpi: RefCell<crate::RelDim>,
36}
37
38impl Default for EditBuffer {
39 fn default() -> Self {
40 Self {
41 buffer: Rc::new(RefCell::new(cosmic_text::Buffer::new_empty(Metrics::new(
42 1.0, 1.0,
43 )))),
44 count: Default::default(),
45 reflow: Default::default(),
46 cursor: Default::default(),
47 select: Default::default(),
48 dpi: RefCell::new(BASE_DPI),
49 }
50 }
51}
52impl Clone for EditBuffer {
53 fn clone(&self) -> Self {
54 Self {
55 buffer: self.buffer.clone(),
56 count: self.count.load(Ordering::Relaxed).into(),
57 reflow: self.reflow.load(Ordering::Relaxed).into(),
58 cursor: self.cursor.load(Ordering::Relaxed).into(),
59 select: self.select.load(Ordering::Relaxed).into(),
60 dpi: self.dpi.clone(),
61 }
62 }
63}
64
65impl EditBuffer {
66 pub fn new(text: &str, cursor: (usize, usize)) -> Self {
67 let this = Self {
68 buffer: Rc::new(RefCell::new(cosmic_text::Buffer::new_empty(Metrics {
69 font_size: 1.0,
70 line_height: 1.0,
71 }))),
72 count: 0.into(),
73 reflow: true.into(),
74 cursor: cursor.0.into(),
75 select: cursor.1.into(),
76 dpi: RefCell::new(BASE_DPI),
77 };
78 this.set_content(text);
79 this
80 }
81 pub fn get_content(&self) -> String {
82 let mut s = String::new();
83 s.reserve(
84 self.buffer
85 .borrow()
86 .lines
87 .iter()
88 .fold(0, |c, l| c + l.text().len() + l.ending().as_str().len()),
89 );
90 for line in &self.buffer.borrow().lines {
91 s.push_str(line.text());
92 s.push_str(line.ending().as_str());
93 }
94 s
95 }
96
97 pub fn set_content(&self, content: &str) {
98 let mut buffer = self.buffer.borrow_mut();
99 buffer.lines.clear();
100 for (range, ending) in cosmic_text::LineIter::new(content) {
101 buffer.lines.push(cosmic_text::BufferLine::new(
102 &content[range],
103 ending,
104 AttrsList::new(&cosmic_text::Attrs::new()),
105 cosmic_text::Shaping::Advanced,
106 ));
107 }
108 if buffer.lines.is_empty() {
109 buffer.lines.push(cosmic_text::BufferLine::new(
110 "",
111 cosmic_text::LineEnding::default(),
112 AttrsList::new(&cosmic_text::Attrs::new()),
113 cosmic_text::Shaping::Advanced,
114 ));
115 }
116 self.reflow.store(true, Ordering::Release);
117 self.count.fetch_add(1, Ordering::Release);
118 }
119
120 pub fn edit(
121 &self,
122 multisplice: &[(ops::Range<usize>, String)],
123 ) -> SmallVec<[(ops::Range<usize>, String); 1]> {
124 let mut text = self.get_content();
125 if multisplice.len() == 1 {
126 let (range, replace) = &multisplice[0];
127 let old = text[range.clone()].to_string();
128 text.replace_range(range.clone(), replace);
129 self.set_content(&text);
130 [(range.start..replace.len(), old)].into()
131 } else {
132 let mut undo = SmallVec::new();
135 let mut last = 0;
136 let mut s = String::new();
137 {
138 for (range, replace) in multisplice {
139 s.push_str(&text[last..range.start]);
140 s.push_str(replace);
141 undo.push((range.start..replace.len(), text[range.clone()].to_string()));
142 last = range.end;
143 }
144
145 s.push_str(&text[last..]);
146 };
147 self.set_content(&s);
148 undo
149 }
150 }
151
152 fn compact(mut idx: usize, affinity: Affinity) -> usize {
153 const FLAG: usize = 1 << (usize::BITS - 1);
154 idx &= !FLAG;
155 if affinity == Affinity::After {
156 idx |= FLAG;
157 }
158 idx
159 }
160 fn expand(cursor: usize) -> (usize, Affinity) {
161 const FLAG: usize = 1 << (usize::BITS - 1);
162 (
163 cursor & (!FLAG),
164 if (cursor & FLAG) != 0 {
165 Affinity::After
166 } else {
167 Affinity::Before
168 },
169 )
170 }
171
172 pub fn get_cursor(&self) -> (usize, Affinity) {
173 Self::expand(self.cursor.load(Ordering::Relaxed))
174 }
175
176 pub fn get_selection(&self) -> (usize, Affinity) {
177 Self::expand(self.select.load(Ordering::Relaxed))
178 }
179
180 pub fn set_cursor(&self, cursor: usize, affinity: Affinity) {
181 let start = Self::compact(cursor, affinity);
182 self.cursor.store(start, Ordering::Release);
183 self.select.store(start, Ordering::Release);
184 self.count.fetch_add(1, Ordering::Release);
185 }
186 pub fn set_selection(&self, start: (usize, Affinity), end: (usize, Affinity)) {
187 let cursor = Self::compact(start.0, start.1);
188 let select = Self::compact(end.0, end.1);
189 self.cursor.store(cursor, Ordering::Release);
190 self.select.store(select, Ordering::Release);
191 self.count.fetch_add(1, Ordering::Release);
192 }
193
194 pub fn to_cursor(buffer: &crate::cosmic_text::Buffer, cursor: (usize, Affinity)) -> Cursor {
195 let mut lines = 0;
196 let (mut idx, mut affinity) = cursor;
197 for line in &buffer.lines {
198 let len = line.text().len();
199 if len >= idx {
200 break;
201 }
202 idx -= len;
203 lines += 1;
204 if idx < line.ending().as_str().len() {
205 affinity = Affinity::Before;
206 idx = 0;
207 break;
208 }
209 idx -= line.ending().as_str().len();
210 }
211 Cursor {
212 line: lines,
213 index: idx,
214 affinity,
215 }
216 }
217
218 pub fn from_cursor(buffer: &crate::cosmic_text::Buffer, cursor: Cursor) -> (usize, Affinity) {
219 let mut idx = 0;
220 for line in buffer.lines.iter().take(cursor.line) {
221 idx += line.text().len() + line.ending().as_str().len();
222 }
223 (idx + cursor.index, cursor.affinity)
224 }
225
226 pub fn flowtext(
227 &self,
228 font_system: &mut crate::cosmic_text::FontSystem,
229 font_size: f32,
230 line_height: f32,
231 wrap: cosmic_text::Wrap,
232 align: Option<cosmic_text::Align>,
233 dpi: crate::RelDim,
234 attrs: cosmic_text::Attrs<'_>,
235 ) {
236 let mut text_buffer = self.buffer.borrow_mut();
237
238 let metrics = cosmic_text::Metrics::new(
239 point_to_pixel(font_size, dpi.width),
240 point_to_pixel(line_height, dpi.height),
241 );
242
243 if text_buffer.metrics() != metrics {
244 text_buffer.set_metrics(font_system, metrics);
245 }
246 *self.dpi.borrow_mut() = dpi;
247
248 if text_buffer.wrap() != wrap {
249 text_buffer.set_wrap(font_system, wrap);
250 }
251 for line in &mut text_buffer.lines {
252 line.set_attrs_list(AttrsList::new(&attrs));
253 line.set_align(align);
254 }
255 text_buffer.shape_until_scroll(font_system, false);
256 self.reflow.store(false, Ordering::Release);
257 }
258}
259
260#[derive(Default, Debug)]
261pub struct EditView {
262 pub(crate) obj: Rc<EditBuffer>,
263 count: usize,
264 reflow: bool,
265 pub(crate) dpi: crate::RelDim,
266}
267
268impl EditView {
269 pub fn get(&self) -> &EditBuffer {
270 &self.obj
271 }
272}
273
274impl Clone for EditView {
276 fn clone(&self) -> Self {
277 Self {
278 obj: self.obj.clone(),
279 count: self.obj.count.load(Ordering::Acquire),
280 reflow: self.obj.reflow.load(Ordering::Acquire),
281 dpi: self.dpi,
282 }
283 }
284}
285
286impl Eq for EditView {}
287impl PartialEq for EditView {
288 fn eq(&self, other: &Self) -> bool {
289 self.count == other.count
290 && self.reflow == other.reflow
291 && Rc::ptr_eq(&self.obj, &other.obj)
292 }
293}
294
295impl From<Rc<EditBuffer>> for EditView {
296 fn from(value: Rc<EditBuffer>) -> Self {
297 Self {
298 obj: value.clone(),
299 count: value.count.load(Ordering::Acquire),
300 reflow: value.reflow.load(Ordering::Acquire),
301 dpi: *value.dpi.borrow(),
302 }
303 }
304}
305
306impl From<EditBuffer> for EditView {
307 fn from(value: EditBuffer) -> Self {
308 let value = Rc::new(value);
309 Self {
310 obj: value.clone(),
311 count: value.count.load(Ordering::Acquire),
312 reflow: value.reflow.load(Ordering::Acquire),
313 dpi: *value.dpi.borrow(),
314 }
315 }
316}