cosmic_text/
buffer_line.rs1#[cfg(not(feature = "std"))]
2use alloc::{string::String, vec::Vec};
3use core::mem;
4
5use crate::{
6 Align, Attrs, AttrsList, Cached, FontSystem, LayoutLine, LineEnding, ShapeLine, Shaping, Wrap,
7};
8
9#[derive(Clone, Debug)]
11pub struct BufferLine {
12 text: String,
13 ending: LineEnding,
14 attrs_list: AttrsList,
15 align: Option<Align>,
16 shape_opt: Cached<ShapeLine>,
17 layout_opt: Cached<Vec<LayoutLine>>,
18 shaping: Shaping,
19 metadata: Option<usize>,
20}
21
22impl BufferLine {
23 pub fn new<T: Into<String>>(
27 text: T,
28 ending: LineEnding,
29 attrs_list: AttrsList,
30 shaping: Shaping,
31 ) -> Self {
32 Self {
33 text: text.into(),
34 ending,
35 attrs_list,
36 align: None,
37 shape_opt: Cached::Empty,
38 layout_opt: Cached::Empty,
39 shaping,
40 metadata: None,
41 }
42 }
43
44 pub fn reset_new<T: Into<String>>(
48 &mut self,
49 text: T,
50 ending: LineEnding,
51 attrs_list: AttrsList,
52 shaping: Shaping,
53 ) {
54 self.text = text.into();
55 self.ending = ending;
56 self.attrs_list = attrs_list;
57 self.align = None;
58 self.shape_opt.set_unused();
59 self.layout_opt.set_unused();
60 self.shaping = shaping;
61 self.metadata = None;
62 }
63
64 pub fn text(&self) -> &str {
66 &self.text
67 }
68
69 pub fn set_text<T: AsRef<str>>(
74 &mut self,
75 text: T,
76 ending: LineEnding,
77 attrs_list: AttrsList,
78 ) -> bool {
79 let text = text.as_ref();
80 if text != self.text || ending != self.ending || attrs_list != self.attrs_list {
81 self.text.clear();
82 self.text.push_str(text);
83 self.ending = ending;
84 self.attrs_list = attrs_list;
85 self.reset();
86 true
87 } else {
88 false
89 }
90 }
91
92 pub fn into_text(self) -> String {
94 self.text
95 }
96
97 pub const fn ending(&self) -> LineEnding {
99 self.ending
100 }
101
102 pub fn set_ending(&mut self, ending: LineEnding) -> bool {
107 if ending != self.ending {
108 self.ending = ending;
109 self.reset_shaping();
110 true
111 } else {
112 false
113 }
114 }
115
116 pub const fn attrs_list(&self) -> &AttrsList {
118 &self.attrs_list
119 }
120
121 pub fn set_attrs_list(&mut self, attrs_list: AttrsList) -> bool {
126 if attrs_list != self.attrs_list {
127 self.attrs_list = attrs_list;
128 self.reset_shaping();
129 true
130 } else {
131 false
132 }
133 }
134
135 pub const fn align(&self) -> Option<Align> {
137 self.align
138 }
139
140 pub fn set_align(&mut self, align: Option<Align>) -> bool {
146 if align != self.align {
147 self.align = align;
148 self.reset_layout();
149 true
150 } else {
151 false
152 }
153 }
154
155 pub fn append(&mut self, other: &Self) {
159 let len = self.text.len();
160 self.text.push_str(other.text());
161
162 self.ending = other.ending();
164
165 if other.attrs_list.defaults() != self.attrs_list.defaults() {
166 self.attrs_list
168 .add_span(len..len + other.text().len(), &other.attrs_list.defaults());
169 }
170
171 for (other_range, attrs) in other.attrs_list.spans_iter() {
172 let range = other_range.start + len..other_range.end + len;
174 self.attrs_list.add_span(range, &attrs.as_attrs());
175 }
176
177 self.reset();
178 }
179
180 pub fn split_off(&mut self, index: usize) -> Self {
182 let text = self.text.split_off(index);
183 let attrs_list = self.attrs_list.split_off(index);
184 self.reset();
185
186 let mut new = Self::new(text, self.ending, attrs_list, self.shaping);
187 self.ending = LineEnding::None;
189 new.align = self.align;
190 new
191 }
192
193 pub fn reset(&mut self) {
195 self.metadata = None;
196 self.reset_shaping();
197 }
198
199 pub fn reset_shaping(&mut self) {
201 self.shape_opt.set_unused();
202 self.reset_layout();
203 }
204
205 pub fn reset_layout(&mut self) {
207 self.layout_opt.set_unused();
208 }
209
210 #[allow(clippy::missing_panics_doc)]
212 pub fn shape(&mut self, font_system: &mut FontSystem, tab_width: u16) -> &ShapeLine {
213 if self.shape_opt.is_unused() {
214 let mut line = self
215 .shape_opt
216 .take_unused()
217 .unwrap_or_else(ShapeLine::empty);
218 line.build(
219 font_system,
220 &self.text,
221 &self.attrs_list,
222 self.shaping,
223 tab_width,
224 );
225 self.shape_opt.set_used(line);
226 self.layout_opt.set_unused();
227 }
228 self.shape_opt.get().expect("shape not found")
229 }
230
231 pub const fn shape_opt(&self) -> Option<&ShapeLine> {
233 self.shape_opt.get()
234 }
235
236 #[allow(clippy::missing_panics_doc)]
238 pub fn layout(
239 &mut self,
240 font_system: &mut FontSystem,
241 font_size: f32,
242 width_opt: Option<f32>,
243 wrap: Wrap,
244 match_mono_width: Option<f32>,
245 tab_width: u16,
246 ) -> &[LayoutLine] {
247 if self.layout_opt.is_unused() {
248 let align = self.align;
249 let mut layout = self
250 .layout_opt
251 .take_unused()
252 .unwrap_or_else(|| Vec::with_capacity(1));
253 let shape = self.shape(font_system, tab_width);
254 shape.layout_to_buffer(
255 &mut font_system.shape_buffer,
256 font_size,
257 width_opt,
258 wrap,
259 align,
260 &mut layout,
261 match_mono_width,
262 );
263 self.layout_opt.set_used(layout);
264 }
265 self.layout_opt.get().expect("layout not found")
266 }
267
268 pub const fn layout_opt(&self) -> Option<&Vec<LayoutLine>> {
270 self.layout_opt.get()
271 }
272
273 pub const fn metadata(&self) -> Option<usize> {
276 self.metadata
277 }
278
279 pub fn set_metadata(&mut self, metadata: usize) {
281 self.metadata = Some(metadata);
282 }
283
284 pub(crate) fn empty() -> Self {
288 Self {
289 text: String::default(),
290 ending: LineEnding::None,
291 attrs_list: AttrsList::new(&Attrs::new()),
292 align: None,
293 shape_opt: Cached::Empty,
294 layout_opt: Cached::Empty,
295 shaping: Shaping::Advanced,
296 metadata: None,
297 }
298 }
299
300 pub(crate) fn reclaim_attrs(&mut self) -> AttrsList {
304 mem::replace(&mut self.attrs_list, AttrsList::new(&Attrs::new()))
305 }
306
307 pub(crate) fn reclaim_text(&mut self) -> String {
311 let mut text = mem::take(&mut self.text);
312 text.clear();
313 text
314 }
315}