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 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 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 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 if other.attrs_list.defaults() != self.attrs_list.defaults() {
163 self.attrs_list
165 .add_span(len..len + other.text().len(), other.attrs_list.defaults());
166 }
167
168 for (other_range, attrs) in other.attrs_list.spans_iter() {
169 let range = other_range.start + len..other_range.end + len;
171 self.attrs_list.add_span(range, attrs.as_attrs());
172 }
173
174 self.reset();
175 }
176
177 pub fn split_off(&mut self, index: usize) -> Self {
179 let text = self.text.split_off(index);
180 let attrs_list = self.attrs_list.split_off(index);
181 self.reset();
182
183 let mut new = Self::new(text, self.ending, attrs_list, self.shaping);
184 new.align = self.align;
185 new
186 }
187
188 pub fn reset(&mut self) {
190 self.metadata = None;
191 self.reset_shaping();
192 }
193
194 pub fn reset_shaping(&mut self) {
196 self.shape_opt.set_unused();
197 self.reset_layout();
198 }
199
200 pub fn reset_layout(&mut self) {
202 self.layout_opt.set_unused();
203 }
204
205 #[allow(clippy::missing_panics_doc)]
207 pub fn shape(&mut self, font_system: &mut FontSystem, tab_width: u16) -> &ShapeLine {
208 if self.shape_opt.is_unused() {
209 let mut line = self
210 .shape_opt
211 .take_unused()
212 .unwrap_or_else(ShapeLine::empty);
213 line.build(
214 font_system,
215 &self.text,
216 &self.attrs_list,
217 self.shaping,
218 tab_width,
219 );
220 self.shape_opt.set_used(line);
221 self.layout_opt.set_unused();
222 }
223 self.shape_opt.get().expect("shape not found")
224 }
225
226 pub fn shape_opt(&self) -> Option<&ShapeLine> {
228 self.shape_opt.get()
229 }
230
231 #[allow(clippy::missing_panics_doc)]
233 pub fn layout(
234 &mut self,
235 font_system: &mut FontSystem,
236 font_size: f32,
237 width_opt: Option<f32>,
238 wrap: Wrap,
239 match_mono_width: Option<f32>,
240 tab_width: u16,
241 ) -> &[LayoutLine] {
242 if self.layout_opt.is_unused() {
243 let align = self.align;
244 let mut layout = self
245 .layout_opt
246 .take_unused()
247 .unwrap_or_else(|| Vec::with_capacity(1));
248 let shape = self.shape(font_system, tab_width);
249 shape.layout_to_buffer(
250 &mut font_system.shape_buffer,
251 font_size,
252 width_opt,
253 wrap,
254 align,
255 &mut layout,
256 match_mono_width,
257 );
258 self.layout_opt.set_used(layout);
259 }
260 self.layout_opt.get().expect("layout not found")
261 }
262
263 pub fn layout_opt(&self) -> Option<&Vec<LayoutLine>> {
265 self.layout_opt.get()
266 }
267
268 pub fn metadata(&self) -> Option<usize> {
271 self.metadata
272 }
273
274 pub fn set_metadata(&mut self, metadata: usize) {
276 self.metadata = Some(metadata);
277 }
278
279 pub(crate) fn empty() -> Self {
283 Self {
284 text: String::default(),
285 ending: LineEnding::default(),
286 attrs_list: AttrsList::new(Attrs::new()),
287 align: None,
288 shape_opt: Cached::Empty,
289 layout_opt: Cached::Empty,
290 shaping: Shaping::Advanced,
291 metadata: None,
292 }
293 }
294
295 pub(crate) fn reclaim_attrs(&mut self) -> AttrsList {
299 mem::replace(&mut self.attrs_list, AttrsList::new(Attrs::new()))
300 }
301
302 pub(crate) fn reclaim_text(&mut self) -> String {
306 let mut text = mem::take(&mut self.text);
307 text.clear();
308 text
309 }
310}