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