1#[cfg(not(feature = "std"))]
4use alloc::{string::String, vec::Vec};
5
6use core::{cmp, fmt};
7
8#[cfg(not(feature = "std"))]
9use core_maths::CoreFloat;
10use unicode_segmentation::UnicodeSegmentation;
11
12use crate::{
13 render_decoration, Affinity, Align, Attrs, AttrsList, BidiParagraphs, BorrowedWithFontSystem,
14 BufferLine, Color, Cursor, DecorationSpan, Ellipsize, FontSystem, Hinting, LayoutCursor,
15 LayoutGlyph, LayoutLine, LineEnding, LineIter, Motion, Renderer, Scroll, ShapeLine, Shaping,
16 Wrap,
17};
18
19#[derive(Debug)]
21pub struct LayoutRun<'a> {
22 pub line_i: usize,
24 pub text: &'a str,
26 pub rtl: bool,
28 pub glyphs: &'a [LayoutGlyph],
30 pub decorations: &'a [DecorationSpan],
32 pub line_y: f32,
34 pub line_top: f32,
36 pub line_height: f32,
38 pub line_w: f32,
40}
41
42impl LayoutRun<'_> {
43 #[allow(clippy::missing_panics_doc)]
48 pub fn highlight(&self, cursor_start: Cursor, cursor_end: Cursor) -> Option<(f32, f32)> {
49 let mut x_start = None;
50 let mut x_end = None;
51 let rtl_factor = if self.rtl { 1. } else { 0. };
52 let ltr_factor = 1. - rtl_factor;
53 for glyph in self.glyphs {
54 let cursor = self.cursor_from_glyph_left(glyph);
55 if cursor >= cursor_start && cursor <= cursor_end {
56 if x_start.is_none() {
57 x_start = Some(glyph.x + glyph.w.mul_add(rtl_factor, 0.0));
58 }
59 x_end = Some(glyph.x + glyph.w.mul_add(rtl_factor, 0.0));
60 }
61 let cursor = self.cursor_from_glyph_right(glyph);
62 if cursor >= cursor_start && cursor <= cursor_end {
63 if x_start.is_none() {
64 x_start = Some(glyph.x + glyph.w.mul_add(ltr_factor, 0.0));
65 }
66 x_end = Some(glyph.x + glyph.w.mul_add(ltr_factor, 0.0));
67 }
68 }
69 x_start.map(|x_start| {
70 let x_end = x_end.expect("end of cursor not found");
71 let (x_start, x_end) = if x_start < x_end {
72 (x_start, x_end)
73 } else {
74 (x_end, x_start)
75 };
76 (x_start, x_end - x_start)
77 })
78 }
79
80 const fn cursor_from_glyph_left(&self, glyph: &LayoutGlyph) -> Cursor {
81 if self.rtl {
82 Cursor::new_with_affinity(self.line_i, glyph.end, Affinity::Before)
83 } else {
84 Cursor::new_with_affinity(self.line_i, glyph.start, Affinity::After)
85 }
86 }
87
88 const fn cursor_from_glyph_right(&self, glyph: &LayoutGlyph) -> Cursor {
89 if self.rtl {
90 Cursor::new_with_affinity(self.line_i, glyph.start, Affinity::After)
91 } else {
92 Cursor::new_with_affinity(self.line_i, glyph.end, Affinity::Before)
93 }
94 }
95}
96
97#[derive(Debug)]
99pub struct LayoutRunIter<'b> {
100 lines: &'b [BufferLine],
101 height_opt: Option<f32>,
102 line_height: f32,
103 scroll: f32,
104 line_i: usize,
105 layout_i: usize,
106 total_height: f32,
107 line_top: f32,
108}
109
110impl<'b> LayoutRunIter<'b> {
111 pub const fn new(buffer: &'b Buffer) -> Self {
112 Self::from_lines(
113 buffer.lines.as_slice(),
114 buffer.height_opt,
115 buffer.metrics.line_height,
116 buffer.scroll.vertical,
117 buffer.scroll.line,
118 )
119 }
120
121 pub const fn from_lines(
122 lines: &'b [BufferLine],
123 height_opt: Option<f32>,
124 line_height: f32,
125 scroll: f32,
126 start: usize,
127 ) -> Self {
128 Self {
129 lines,
130 height_opt,
131 line_height,
132 scroll,
133 line_i: start,
134 layout_i: 0,
135 total_height: 0.0,
136 line_top: 0.0,
137 }
138 }
139}
140
141impl<'b> Iterator for LayoutRunIter<'b> {
142 type Item = LayoutRun<'b>;
143
144 fn next(&mut self) -> Option<Self::Item> {
145 while let Some(line) = self.lines.get(self.line_i) {
146 let shape = line.shape_opt()?;
147 let layout = line.layout_opt()?;
148 while let Some(layout_line) = layout.get(self.layout_i) {
149 self.layout_i += 1;
150
151 let line_height = layout_line.line_height_opt.unwrap_or(self.line_height);
152 self.total_height += line_height;
153
154 let line_top = self.line_top - self.scroll;
155 let glyph_height = layout_line.max_ascent + layout_line.max_descent;
156 let centering_offset = (line_height - glyph_height) / 2.0;
157 let line_y = line_top + centering_offset + layout_line.max_ascent;
158 if let Some(height) = self.height_opt {
159 if line_y - layout_line.max_ascent > height {
160 return None;
161 }
162 }
163 self.line_top += line_height;
164 if line_y + layout_line.max_descent < 0.0 {
165 continue;
166 }
167
168 return Some(LayoutRun {
169 line_i: self.line_i,
170 text: line.text(),
171 rtl: shape.rtl,
172 glyphs: &layout_line.glyphs,
173 decorations: &layout_line.decorations,
174 line_y,
175 line_top,
176 line_height,
177 line_w: layout_line.w,
178 });
179 }
180 self.line_i += 1;
181 self.layout_i = 0;
182 }
183
184 None
185 }
186}
187
188#[derive(Clone, Copy, Debug, Default, PartialEq)]
190pub struct Metrics {
191 pub font_size: f32,
193 pub line_height: f32,
195}
196
197impl Metrics {
198 pub const fn new(font_size: f32, line_height: f32) -> Self {
200 Self {
201 font_size,
202 line_height,
203 }
204 }
205
206 pub fn relative(font_size: f32, line_height_scale: f32) -> Self {
208 Self {
209 font_size,
210 line_height: font_size * line_height_scale,
211 }
212 }
213
214 pub fn scale(self, scale: f32) -> Self {
216 Self {
217 font_size: self.font_size * scale,
218 line_height: self.line_height * scale,
219 }
220 }
221}
222
223impl fmt::Display for Metrics {
224 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
225 write!(f, "{}px / {}px", self.font_size, self.line_height)
226 }
227}
228
229#[derive(Debug)]
231pub struct Buffer {
232 pub lines: Vec<BufferLine>,
234 metrics: Metrics,
235 width_opt: Option<f32>,
236 height_opt: Option<f32>,
237 scroll: Scroll,
238 redraw: bool,
240 wrap: Wrap,
241 ellipsize: Ellipsize,
242 monospace_width: Option<f32>,
243 tab_width: u16,
244 hinting: Hinting,
245}
246
247impl Clone for Buffer {
248 fn clone(&self) -> Self {
249 Self {
250 lines: self.lines.clone(),
251 metrics: self.metrics,
252 width_opt: self.width_opt,
253 height_opt: self.height_opt,
254 scroll: self.scroll,
255 redraw: self.redraw,
256 wrap: self.wrap,
257 ellipsize: self.ellipsize,
258 monospace_width: self.monospace_width,
259 tab_width: self.tab_width,
260 hinting: self.hinting,
261 }
262 }
263}
264
265impl Buffer {
266 pub fn new_empty(metrics: Metrics) -> Self {
278 assert_ne!(metrics.line_height, 0.0, "line height cannot be 0");
279 Self {
280 lines: Vec::new(),
281 metrics,
282 width_opt: None,
283 height_opt: None,
284 scroll: Scroll::default(),
285 redraw: false,
286 wrap: Wrap::WordOrGlyph,
287 ellipsize: Ellipsize::None,
288 monospace_width: None,
289 tab_width: 8,
290 hinting: Hinting::default(),
291 }
292 }
293
294 pub fn new(font_system: &mut FontSystem, metrics: Metrics) -> Self {
300 let mut buffer = Self::new_empty(metrics);
301 buffer.set_text(font_system, "", &Attrs::new(), Shaping::Advanced, None);
302 buffer
303 }
304
305 pub fn borrow_with<'a>(
307 &'a mut self,
308 font_system: &'a mut FontSystem,
309 ) -> BorrowedWithFontSystem<'a, Self> {
310 BorrowedWithFontSystem {
311 inner: self,
312 font_system,
313 }
314 }
315
316 fn relayout(&mut self, font_system: &mut FontSystem) {
317 #[cfg(all(feature = "std", not(target_arch = "wasm32")))]
318 let instant = std::time::Instant::now();
319
320 for line in &mut self.lines {
321 if line.shape_opt().is_some() {
322 line.reset_layout();
323 line.layout(
324 font_system,
325 self.metrics.font_size,
326 self.width_opt,
327 self.wrap,
328 self.ellipsize,
329 self.monospace_width,
330 self.tab_width,
331 self.hinting,
332 );
333 }
334 }
335
336 self.redraw = true;
337
338 #[cfg(all(feature = "std", not(target_arch = "wasm32")))]
339 log::debug!("relayout: {:?}", instant.elapsed());
340 }
341
342 #[allow(clippy::missing_panics_doc)]
344 pub fn shape_until_cursor(
345 &mut self,
346 font_system: &mut FontSystem,
347 cursor: Cursor,
348 prune: bool,
349 ) {
350 let metrics = self.metrics;
351 let old_scroll = self.scroll;
352
353 let layout_cursor = self
354 .layout_cursor(font_system, cursor)
355 .expect("shape_until_cursor invalid cursor");
356
357 let mut layout_y = 0.0;
358 let mut total_height = {
359 let layout = self
360 .line_layout(font_system, layout_cursor.line)
361 .expect("shape_until_cursor failed to scroll forwards");
362 (0..layout_cursor.layout).for_each(|layout_i| {
363 layout_y += layout[layout_i]
364 .line_height_opt
365 .unwrap_or(metrics.line_height);
366 });
367 layout_y
368 + layout[layout_cursor.layout]
369 .line_height_opt
370 .unwrap_or(metrics.line_height)
371 };
372
373 if self.scroll.line > layout_cursor.line
374 || (self.scroll.line == layout_cursor.line && self.scroll.vertical > layout_y)
375 {
376 self.scroll.line = layout_cursor.line;
378 self.scroll.vertical = layout_y;
379 } else if let Some(height) = self.height_opt {
380 let mut line_i = layout_cursor.line;
382 if line_i <= self.scroll.line {
383 if total_height > height + self.scroll.vertical {
385 self.scroll.vertical = total_height - height;
386 }
387 } else {
388 while line_i > self.scroll.line {
389 line_i -= 1;
390 let layout = self
391 .line_layout(font_system, line_i)
392 .expect("shape_until_cursor failed to scroll forwards");
393 for layout_line in layout {
394 total_height += layout_line.line_height_opt.unwrap_or(metrics.line_height);
395 }
396 if total_height > height + self.scroll.vertical {
397 self.scroll.line = line_i;
398 self.scroll.vertical = total_height - height;
399 }
400 }
401 }
402 }
403
404 if old_scroll != self.scroll {
405 self.redraw = true;
406 }
407
408 self.shape_until_scroll(font_system, prune);
409
410 if let Some(layout_cursor) = self.layout_cursor(font_system, cursor) {
412 if let Some(layout_lines) = self.line_layout(font_system, layout_cursor.line) {
413 if let Some(layout_line) = layout_lines.get(layout_cursor.layout) {
414 let (x_min, x_max) = layout_line
415 .glyphs
416 .get(layout_cursor.glyph)
417 .or_else(|| layout_line.glyphs.last())
418 .map_or((0.0, 0.0), |glyph| {
419 let x_a = glyph.x;
421 let x_b = glyph.x + glyph.w;
422 (x_a.min(x_b), x_a.max(x_b))
423 });
424 if x_min < self.scroll.horizontal {
425 self.scroll.horizontal = x_min;
426 self.redraw = true;
427 }
428 if let Some(width) = self.width_opt {
429 if x_max > self.scroll.horizontal + width {
430 self.scroll.horizontal = x_max - width;
431 self.redraw = true;
432 }
433 }
434 }
435 }
436 }
437 }
438
439 #[allow(clippy::missing_panics_doc)]
441 pub fn shape_until_scroll(&mut self, font_system: &mut FontSystem, prune: bool) {
442 let metrics = self.metrics;
443 let old_scroll = self.scroll;
444
445 loop {
446 while self.scroll.vertical < 0.0 {
448 if self.scroll.line > 0 {
449 let line_i = self.scroll.line - 1;
450 if let Some(layout) = self.line_layout(font_system, line_i) {
451 let mut layout_height = 0.0;
452 for layout_line in layout {
453 layout_height +=
454 layout_line.line_height_opt.unwrap_or(metrics.line_height);
455 }
456 self.scroll.line = line_i;
457 self.scroll.vertical += layout_height;
458 } else {
459 self.scroll.line = line_i;
461 self.scroll.vertical += metrics.line_height;
462 }
463 } else {
464 self.scroll.vertical = 0.0;
465 break;
466 }
467 }
468
469 let scroll_start = self.scroll.vertical;
470 let scroll_end = scroll_start + self.height_opt.unwrap_or(f32::INFINITY);
471
472 let mut total_height = 0.0;
473 for line_i in 0..self.lines.len() {
474 if line_i < self.scroll.line {
475 if prune {
476 self.lines[line_i].reset_shaping();
477 }
478 continue;
479 }
480 if total_height > scroll_end {
481 if prune {
482 self.lines[line_i].reset_shaping();
483 continue;
484 }
485 break;
486 }
487
488 let mut layout_height = 0.0;
489 let layout = self
490 .line_layout(font_system, line_i)
491 .expect("shape_until_scroll invalid line");
492 for layout_line in layout {
493 let line_height = layout_line.line_height_opt.unwrap_or(metrics.line_height);
494 layout_height += line_height;
495 total_height += line_height;
496 }
497
498 if line_i == self.scroll.line && layout_height <= self.scroll.vertical {
500 self.scroll.line += 1;
501 self.scroll.vertical -= layout_height;
502 }
503 }
504
505 if total_height < scroll_end && self.scroll.line > 0 {
506 self.scroll.vertical -= scroll_end - total_height;
508 } else {
509 break;
511 }
512 }
513
514 if old_scroll != self.scroll {
515 self.redraw = true;
516 }
517 }
518
519 pub fn layout_cursor(
521 &mut self,
522 font_system: &mut FontSystem,
523 cursor: Cursor,
524 ) -> Option<LayoutCursor> {
525 let layout = self.line_layout(font_system, cursor.line)?;
526 for (layout_i, layout_line) in layout.iter().enumerate() {
527 for (glyph_i, glyph) in layout_line.glyphs.iter().enumerate() {
528 let cursor_end =
529 Cursor::new_with_affinity(cursor.line, glyph.end, Affinity::Before);
530 let cursor_start =
531 Cursor::new_with_affinity(cursor.line, glyph.start, Affinity::After);
532 let (cursor_left, cursor_right) = if glyph.level.is_ltr() {
533 (cursor_start, cursor_end)
534 } else {
535 (cursor_end, cursor_start)
536 };
537 if cursor == cursor_left {
538 return Some(LayoutCursor::new(cursor.line, layout_i, glyph_i));
539 }
540 if cursor == cursor_right {
541 return Some(LayoutCursor::new(cursor.line, layout_i, glyph_i + 1));
542 }
543 }
544 }
545
546 Some(LayoutCursor::new(cursor.line, 0, 0))
549 }
550
551 pub fn line_shape(
553 &mut self,
554 font_system: &mut FontSystem,
555 line_i: usize,
556 ) -> Option<&ShapeLine> {
557 let line = self.lines.get_mut(line_i)?;
558 Some(line.shape(font_system, self.tab_width))
559 }
560
561 pub fn line_layout(
563 &mut self,
564 font_system: &mut FontSystem,
565 line_i: usize,
566 ) -> Option<&[LayoutLine]> {
567 let line = self.lines.get_mut(line_i)?;
568 Some(line.layout(
569 font_system,
570 self.metrics.font_size,
571 self.width_opt,
572 self.wrap,
573 self.ellipsize,
574 self.monospace_width,
575 self.tab_width,
576 self.hinting,
577 ))
578 }
579
580 pub const fn metrics(&self) -> Metrics {
582 self.metrics
583 }
584
585 pub fn set_metrics(&mut self, font_system: &mut FontSystem, metrics: Metrics) {
591 self.set_metrics_and_size(font_system, metrics, self.width_opt, self.height_opt);
592 }
593
594 pub const fn hinting(&self) -> Hinting {
596 self.hinting
597 }
598
599 pub fn set_hinting(&mut self, font_system: &mut FontSystem, hinting: Hinting) {
601 if hinting != self.hinting {
602 self.hinting = hinting;
603 self.relayout(font_system);
604 self.shape_until_scroll(font_system, false);
605 }
606 }
607
608 pub const fn wrap(&self) -> Wrap {
610 self.wrap
611 }
612
613 pub fn set_wrap(&mut self, font_system: &mut FontSystem, wrap: Wrap) {
615 if wrap != self.wrap {
616 self.wrap = wrap;
617 self.relayout(font_system);
618 self.shape_until_scroll(font_system, false);
619 }
620 }
621
622 pub const fn ellipsize(&self) -> Ellipsize {
624 self.ellipsize
625 }
626
627 pub fn set_ellipsize(&mut self, font_system: &mut FontSystem, ellipsize: Ellipsize) {
629 if ellipsize != self.ellipsize {
630 self.ellipsize = ellipsize;
631 self.relayout(font_system);
632 self.shape_until_scroll(font_system, false);
633 }
634 }
635
636 pub const fn monospace_width(&self) -> Option<f32> {
638 self.monospace_width
639 }
640
641 pub fn set_monospace_width(
643 &mut self,
644 font_system: &mut FontSystem,
645 monospace_width: Option<f32>,
646 ) {
647 if monospace_width != self.monospace_width {
648 self.monospace_width = monospace_width;
649 self.relayout(font_system);
650 self.shape_until_scroll(font_system, false);
651 }
652 }
653
654 pub const fn tab_width(&self) -> u16 {
656 self.tab_width
657 }
658
659 pub fn set_tab_width(&mut self, font_system: &mut FontSystem, tab_width: u16) {
661 if tab_width == 0 {
663 return;
664 }
665 if tab_width != self.tab_width {
666 self.tab_width = tab_width;
667 for line in &mut self.lines {
669 if line.shape_opt().is_some() && line.text().contains('\t') {
670 line.reset_shaping();
671 }
672 }
673 self.redraw = true;
674 self.shape_until_scroll(font_system, false);
675 }
676 }
677
678 pub const fn size(&self) -> (Option<f32>, Option<f32>) {
680 (self.width_opt, self.height_opt)
681 }
682
683 pub fn set_size(
685 &mut self,
686 font_system: &mut FontSystem,
687 width_opt: Option<f32>,
688 height_opt: Option<f32>,
689 ) {
690 self.set_metrics_and_size(font_system, self.metrics, width_opt, height_opt);
691 }
692
693 pub fn set_metrics_and_size(
699 &mut self,
700 font_system: &mut FontSystem,
701 metrics: Metrics,
702 width_opt: Option<f32>,
703 height_opt: Option<f32>,
704 ) {
705 let clamped_width_opt = width_opt.map(|width| width.max(0.0));
706 let clamped_height_opt = height_opt.map(|height| height.max(0.0));
707
708 if metrics != self.metrics
709 || clamped_width_opt != self.width_opt
710 || clamped_height_opt != self.height_opt
711 {
712 assert_ne!(metrics.font_size, 0.0, "font size cannot be 0");
713 self.metrics = metrics;
714 self.width_opt = clamped_width_opt;
715 self.height_opt = clamped_height_opt;
716 self.relayout(font_system);
717 self.shape_until_scroll(font_system, false);
718 }
719 }
720
721 pub const fn scroll(&self) -> Scroll {
723 self.scroll
724 }
725
726 pub fn set_scroll(&mut self, scroll: Scroll) {
728 if scroll != self.scroll {
729 self.scroll = scroll;
730 self.redraw = true;
731 }
732 }
733
734 pub fn set_text(
736 &mut self,
737 font_system: &mut FontSystem,
738 text: &str,
739 attrs: &Attrs,
740 shaping: Shaping,
741 alignment: Option<Align>,
742 ) {
743 self.lines.clear();
744 for (range, ending) in LineIter::new(text) {
745 self.lines.push(BufferLine::new(
746 &text[range],
747 ending,
748 AttrsList::new(attrs),
749 shaping,
750 ));
751 }
752
753 if self
755 .lines
756 .last()
757 .map(|line| line.ending())
758 .unwrap_or_default()
759 != LineEnding::None
760 {
761 self.lines.push(BufferLine::new(
762 "",
763 LineEnding::None,
764 AttrsList::new(attrs),
765 shaping,
766 ));
767 }
768
769 if alignment.is_some() {
770 self.lines.iter_mut().for_each(|line| {
771 line.set_align(alignment);
772 });
773 }
774
775 self.scroll = Scroll::default();
776 self.shape_until_scroll(font_system, false);
777 }
778
779 pub fn set_rich_text<'r, 's, I>(
798 &mut self,
799 font_system: &mut FontSystem,
800 spans: I,
801 default_attrs: &Attrs,
802 shaping: Shaping,
803 alignment: Option<Align>,
804 ) where
805 I: IntoIterator<Item = (&'s str, Attrs<'r>)>,
806 {
807 let mut end = 0;
808 let (string, spans_data): (String, Vec<_>) = spans
810 .into_iter()
811 .map(|(s, attrs)| {
812 let start = end;
813 end += s.len();
814 (s, (attrs, start..end))
815 })
816 .unzip();
817
818 let mut spans_iter = spans_data.into_iter();
819 let mut maybe_span = spans_iter.next();
820
821 let string_start = string.as_ptr() as usize;
823 let mut lines_iter = BidiParagraphs::new(&string).map(|line: &str| {
824 let start = line.as_ptr() as usize - string_start;
825 let end = start + line.len();
826 start..end
827 });
828 let mut maybe_line = lines_iter.next();
829 let line_ending = LineEnding::default();
831
832 let mut line_count = 0;
833 let mut attrs_list = self
834 .lines
835 .get_mut(line_count)
836 .map_or_else(|| AttrsList::new(&Attrs::new()), BufferLine::reclaim_attrs)
837 .reset(default_attrs);
838 let mut line_string = self
839 .lines
840 .get_mut(line_count)
841 .map(BufferLine::reclaim_text)
842 .unwrap_or_default();
843
844 loop {
845 let (Some(line_range), Some((attrs, span_range))) = (&maybe_line, &maybe_span) else {
846 if self.lines.len() == line_count {
848 self.lines.push(BufferLine::empty());
849 }
850 self.lines[line_count].reset_new(
851 String::new(),
852 line_ending,
853 AttrsList::new(default_attrs),
854 shaping,
855 );
856 line_count += 1;
857 break;
858 };
859
860 let start = line_range.start.max(span_range.start);
862 let end = line_range.end.min(span_range.end);
863 if start < end {
864 let text = &string[start..end];
865 let text_start = line_string.len();
866 line_string.push_str(text);
867 let text_end = line_string.len();
868 if *attrs != attrs_list.defaults() {
870 attrs_list.add_span(text_start..text_end, attrs);
871 }
872 } else if line_string.is_empty() && attrs.metrics_opt.is_some() {
873 attrs_list = attrs_list.reset(attrs);
877 }
878
879 if span_range.end < line_range.end {
885 maybe_span = spans_iter.next();
886 } else {
887 maybe_line = lines_iter.next();
888 if maybe_line.is_some() {
889 let next_attrs_list = self
891 .lines
892 .get_mut(line_count + 1)
893 .map_or_else(|| AttrsList::new(&Attrs::new()), BufferLine::reclaim_attrs)
894 .reset(default_attrs);
895 let next_line_string = self
896 .lines
897 .get_mut(line_count + 1)
898 .map(BufferLine::reclaim_text)
899 .unwrap_or_default();
900 let prev_attrs_list = core::mem::replace(&mut attrs_list, next_attrs_list);
901 let prev_line_string = core::mem::replace(&mut line_string, next_line_string);
902 if self.lines.len() == line_count {
903 self.lines.push(BufferLine::empty());
904 }
905 self.lines[line_count].reset_new(
906 prev_line_string,
907 line_ending,
908 prev_attrs_list,
909 shaping,
910 );
911 line_count += 1;
912 } else {
913 if self.lines.len() == line_count {
915 self.lines.push(BufferLine::empty());
916 }
917 self.lines[line_count].reset_new(line_string, line_ending, attrs_list, shaping);
918 line_count += 1;
919 break;
920 }
921 }
922 }
923
924 self.lines.truncate(line_count);
926
927 self.lines.iter_mut().for_each(|line| {
928 line.set_align(alignment);
929 });
930
931 self.scroll = Scroll::default();
932
933 self.shape_until_scroll(font_system, false);
934 }
935
936 pub const fn redraw(&self) -> bool {
938 self.redraw
939 }
940
941 pub fn set_redraw(&mut self, redraw: bool) {
943 self.redraw = redraw;
944 }
945
946 pub fn layout_runs(&self) -> LayoutRunIter<'_> {
948 LayoutRunIter::new(self)
949 }
950
951 pub fn hit(&self, x: f32, y: f32) -> Option<Cursor> {
953 #[cfg(all(feature = "std", not(target_arch = "wasm32")))]
954 let instant = std::time::Instant::now();
955
956 let mut new_cursor_opt = None;
957
958 let mut runs = self.layout_runs().peekable();
959 let mut first_run = true;
960 while let Some(run) = runs.next() {
961 let line_top = run.line_top;
962 let line_height = run.line_height;
963
964 if first_run && y < line_top {
965 first_run = false;
966 let new_cursor = Cursor::new(run.line_i, 0);
967 new_cursor_opt = Some(new_cursor);
968 } else if y >= line_top && y < line_top + line_height {
969 let mut new_cursor_glyph = run.glyphs.len();
970 let mut new_cursor_char = 0;
971 let mut new_cursor_affinity = Affinity::After;
972
973 let mut first_glyph = true;
974
975 'hit: for (glyph_i, glyph) in run.glyphs.iter().enumerate() {
976 if first_glyph {
977 first_glyph = false;
978 if (run.rtl && x > glyph.x) || (!run.rtl && x < 0.0) {
979 new_cursor_glyph = 0;
980 new_cursor_char = 0;
981 }
982 }
983 if x >= glyph.x && x <= glyph.x + glyph.w {
984 new_cursor_glyph = glyph_i;
985
986 let cluster = &run.text[glyph.start..glyph.end];
987 let total = cluster.grapheme_indices(true).count();
988 let mut egc_x = glyph.x;
989 let egc_w = glyph.w / (total as f32);
990 for (egc_i, egc) in cluster.grapheme_indices(true) {
991 if x >= egc_x && x <= egc_x + egc_w {
992 new_cursor_char = egc_i;
993
994 let right_half = x >= egc_x + egc_w / 2.0;
995 if right_half != glyph.level.is_rtl() {
996 new_cursor_char += egc.len();
998 new_cursor_affinity = Affinity::Before;
999 }
1000 break 'hit;
1001 }
1002 egc_x += egc_w;
1003 }
1004
1005 let right_half = x >= glyph.x + glyph.w / 2.0;
1006 if right_half != glyph.level.is_rtl() {
1007 new_cursor_char = cluster.len();
1009 new_cursor_affinity = Affinity::Before;
1010 }
1011 break 'hit;
1012 }
1013 }
1014
1015 let mut new_cursor = Cursor::new(run.line_i, 0);
1016
1017 match run.glyphs.get(new_cursor_glyph) {
1018 Some(glyph) => {
1019 new_cursor.index = glyph.start + new_cursor_char;
1021 new_cursor.affinity = new_cursor_affinity;
1022 }
1023 None => {
1024 if let Some(glyph) = run.glyphs.last() {
1025 new_cursor.index = glyph.end;
1027 new_cursor.affinity = Affinity::Before;
1028 }
1029 }
1030 }
1031
1032 new_cursor_opt = Some(new_cursor);
1033
1034 break;
1035 } else if runs.peek().is_none() && y > run.line_y {
1036 let mut new_cursor = Cursor::new(run.line_i, 0);
1037 if let Some(glyph) = run.glyphs.last() {
1038 new_cursor = run.cursor_from_glyph_right(glyph);
1039 }
1040 new_cursor_opt = Some(new_cursor);
1041 }
1042 }
1043
1044 #[cfg(all(feature = "std", not(target_arch = "wasm32")))]
1045 log::trace!("click({}, {}): {:?}", x, y, instant.elapsed());
1046
1047 new_cursor_opt
1048 }
1049
1050 pub fn cursor_motion(
1052 &mut self,
1053 font_system: &mut FontSystem,
1054 mut cursor: Cursor,
1055 mut cursor_x_opt: Option<i32>,
1056 motion: Motion,
1057 ) -> Option<(Cursor, Option<i32>)> {
1058 match motion {
1059 Motion::LayoutCursor(layout_cursor) => {
1060 let layout = self.line_layout(font_system, layout_cursor.line)?;
1061
1062 let layout_line = match layout.get(layout_cursor.layout) {
1063 Some(some) => some,
1064 None => match layout.last() {
1065 Some(some) => some,
1066 None => {
1067 return None;
1068 }
1069 },
1070 };
1071
1072 let (new_index, new_affinity) =
1073 layout_line.glyphs.get(layout_cursor.glyph).map_or_else(
1074 || {
1075 layout_line
1076 .glyphs
1077 .last()
1078 .map_or((0, Affinity::After), |glyph| (glyph.end, Affinity::Before))
1079 },
1080 |glyph| (glyph.start, Affinity::After),
1081 );
1082
1083 if cursor.line != layout_cursor.line
1084 || cursor.index != new_index
1085 || cursor.affinity != new_affinity
1086 {
1087 cursor.line = layout_cursor.line;
1088 cursor.index = new_index;
1089 cursor.affinity = new_affinity;
1090 }
1091 }
1092 Motion::Previous => {
1093 let line = self.lines.get(cursor.line)?;
1094 if cursor.index > 0 {
1095 let mut prev_index = 0;
1097 for (i, _) in line.text().grapheme_indices(true) {
1098 if i < cursor.index {
1099 prev_index = i;
1100 } else {
1101 break;
1102 }
1103 }
1104
1105 cursor.index = prev_index;
1106 cursor.affinity = Affinity::After;
1107 } else if cursor.line > 0 {
1108 cursor.line -= 1;
1109 cursor.index = self.lines.get(cursor.line)?.text().len();
1110 cursor.affinity = Affinity::After;
1111 }
1112 cursor_x_opt = None;
1113 }
1114 Motion::Next => {
1115 let line = self.lines.get(cursor.line)?;
1116 if cursor.index < line.text().len() {
1117 for (i, c) in line.text().grapheme_indices(true) {
1118 if i == cursor.index {
1119 cursor.index += c.len();
1120 cursor.affinity = Affinity::Before;
1121 break;
1122 }
1123 }
1124 } else if cursor.line + 1 < self.lines.len() {
1125 cursor.line += 1;
1126 cursor.index = 0;
1127 cursor.affinity = Affinity::Before;
1128 }
1129 cursor_x_opt = None;
1130 }
1131 Motion::Left => {
1132 let rtl_opt = self
1133 .line_shape(font_system, cursor.line)
1134 .map(|shape| shape.rtl);
1135 if let Some(rtl) = rtl_opt {
1136 if rtl {
1137 (cursor, cursor_x_opt) =
1138 self.cursor_motion(font_system, cursor, cursor_x_opt, Motion::Next)?;
1139 } else {
1140 (cursor, cursor_x_opt) = self.cursor_motion(
1141 font_system,
1142 cursor,
1143 cursor_x_opt,
1144 Motion::Previous,
1145 )?;
1146 }
1147 }
1148 }
1149 Motion::Right => {
1150 let rtl_opt = self
1151 .line_shape(font_system, cursor.line)
1152 .map(|shape| shape.rtl);
1153 if let Some(rtl) = rtl_opt {
1154 if rtl {
1155 (cursor, cursor_x_opt) = self.cursor_motion(
1156 font_system,
1157 cursor,
1158 cursor_x_opt,
1159 Motion::Previous,
1160 )?;
1161 } else {
1162 (cursor, cursor_x_opt) =
1163 self.cursor_motion(font_system, cursor, cursor_x_opt, Motion::Next)?;
1164 }
1165 }
1166 }
1167 Motion::Up => {
1168 let mut layout_cursor = self.layout_cursor(font_system, cursor)?;
1169
1170 if cursor_x_opt.is_none() {
1171 cursor_x_opt = Some(
1172 layout_cursor.glyph as i32, );
1174 }
1175
1176 if layout_cursor.layout > 0 {
1177 layout_cursor.layout -= 1;
1178 } else if layout_cursor.line > 0 {
1179 layout_cursor.line -= 1;
1180 layout_cursor.layout = usize::MAX;
1181 }
1182
1183 if let Some(cursor_x) = cursor_x_opt {
1184 layout_cursor.glyph = cursor_x as usize; }
1186
1187 (cursor, cursor_x_opt) = self.cursor_motion(
1188 font_system,
1189 cursor,
1190 cursor_x_opt,
1191 Motion::LayoutCursor(layout_cursor),
1192 )?;
1193 }
1194 Motion::Down => {
1195 let mut layout_cursor = self.layout_cursor(font_system, cursor)?;
1196
1197 let layout_len = self.line_layout(font_system, layout_cursor.line)?.len();
1198
1199 if cursor_x_opt.is_none() {
1200 cursor_x_opt = Some(
1201 layout_cursor.glyph as i32, );
1203 }
1204
1205 if layout_cursor.layout + 1 < layout_len {
1206 layout_cursor.layout += 1;
1207 } else if layout_cursor.line + 1 < self.lines.len() {
1208 layout_cursor.line += 1;
1209 layout_cursor.layout = 0;
1210 }
1211
1212 if let Some(cursor_x) = cursor_x_opt {
1213 layout_cursor.glyph = cursor_x as usize; }
1215
1216 (cursor, cursor_x_opt) = self.cursor_motion(
1217 font_system,
1218 cursor,
1219 cursor_x_opt,
1220 Motion::LayoutCursor(layout_cursor),
1221 )?;
1222 }
1223 Motion::Home => {
1224 cursor.index = 0;
1225 cursor_x_opt = None;
1226 }
1227 Motion::SoftHome => {
1228 let line = self.lines.get(cursor.line)?;
1229 cursor.index = line
1230 .text()
1231 .char_indices()
1232 .find_map(|(i, c)| if c.is_whitespace() { None } else { Some(i) })
1233 .unwrap_or(0);
1234 cursor_x_opt = None;
1235 }
1236 Motion::End => {
1237 let line = self.lines.get(cursor.line)?;
1238 cursor.index = line.text().len();
1239 cursor_x_opt = None;
1240 }
1241 Motion::ParagraphStart => {
1242 cursor.index = 0;
1243 cursor_x_opt = None;
1244 }
1245 Motion::ParagraphEnd => {
1246 cursor.index = self.lines.get(cursor.line)?.text().len();
1247 cursor_x_opt = None;
1248 }
1249 Motion::PageUp => {
1250 if let Some(height) = self.height_opt {
1251 (cursor, cursor_x_opt) = self.cursor_motion(
1252 font_system,
1253 cursor,
1254 cursor_x_opt,
1255 Motion::Vertical(-height as i32),
1256 )?;
1257 }
1258 }
1259 Motion::PageDown => {
1260 if let Some(height) = self.height_opt {
1261 (cursor, cursor_x_opt) = self.cursor_motion(
1262 font_system,
1263 cursor,
1264 cursor_x_opt,
1265 Motion::Vertical(height as i32),
1266 )?;
1267 }
1268 }
1269 Motion::Vertical(px) => {
1270 let lines = px / self.metrics().line_height as i32;
1272 match lines.cmp(&0) {
1273 cmp::Ordering::Less => {
1274 for _ in 0..-lines {
1275 (cursor, cursor_x_opt) =
1276 self.cursor_motion(font_system, cursor, cursor_x_opt, Motion::Up)?;
1277 }
1278 }
1279 cmp::Ordering::Greater => {
1280 for _ in 0..lines {
1281 (cursor, cursor_x_opt) = self.cursor_motion(
1282 font_system,
1283 cursor,
1284 cursor_x_opt,
1285 Motion::Down,
1286 )?;
1287 }
1288 }
1289 cmp::Ordering::Equal => {}
1290 }
1291 }
1292 Motion::PreviousWord => {
1293 let line = self.lines.get(cursor.line)?;
1294 if cursor.index > 0 {
1295 cursor.index = line
1296 .text()
1297 .unicode_word_indices()
1298 .rev()
1299 .map(|(i, _)| i)
1300 .find(|&i| i < cursor.index)
1301 .unwrap_or(0);
1302 } else if cursor.line > 0 {
1303 cursor.line -= 1;
1304 cursor.index = self.lines.get(cursor.line)?.text().len();
1305 }
1306 cursor_x_opt = None;
1307 }
1308 Motion::NextWord => {
1309 let line = self.lines.get(cursor.line)?;
1310 if cursor.index < line.text().len() {
1311 cursor.index = line
1312 .text()
1313 .unicode_word_indices()
1314 .map(|(i, word)| i + word.len())
1315 .find(|&i| i > cursor.index)
1316 .unwrap_or_else(|| line.text().len());
1317 } else if cursor.line + 1 < self.lines.len() {
1318 cursor.line += 1;
1319 cursor.index = 0;
1320 }
1321 cursor_x_opt = None;
1322 }
1323 Motion::LeftWord => {
1324 let rtl_opt = self
1325 .line_shape(font_system, cursor.line)
1326 .map(|shape| shape.rtl);
1327 if let Some(rtl) = rtl_opt {
1328 if rtl {
1329 (cursor, cursor_x_opt) = self.cursor_motion(
1330 font_system,
1331 cursor,
1332 cursor_x_opt,
1333 Motion::NextWord,
1334 )?;
1335 } else {
1336 (cursor, cursor_x_opt) = self.cursor_motion(
1337 font_system,
1338 cursor,
1339 cursor_x_opt,
1340 Motion::PreviousWord,
1341 )?;
1342 }
1343 }
1344 }
1345 Motion::RightWord => {
1346 let rtl_opt = self
1347 .line_shape(font_system, cursor.line)
1348 .map(|shape| shape.rtl);
1349 if let Some(rtl) = rtl_opt {
1350 if rtl {
1351 (cursor, cursor_x_opt) = self.cursor_motion(
1352 font_system,
1353 cursor,
1354 cursor_x_opt,
1355 Motion::PreviousWord,
1356 )?;
1357 } else {
1358 (cursor, cursor_x_opt) = self.cursor_motion(
1359 font_system,
1360 cursor,
1361 cursor_x_opt,
1362 Motion::NextWord,
1363 )?;
1364 }
1365 }
1366 }
1367 Motion::BufferStart => {
1368 cursor.line = 0;
1369 cursor.index = 0;
1370 cursor_x_opt = None;
1371 }
1372 Motion::BufferEnd => {
1373 cursor.line = self.lines.len().saturating_sub(1);
1374 cursor.index = self.lines.get(cursor.line)?.text().len();
1375 cursor_x_opt = None;
1376 }
1377 Motion::GotoLine(line) => {
1378 let mut layout_cursor = self.layout_cursor(font_system, cursor)?;
1379 layout_cursor.line = line;
1380 (cursor, cursor_x_opt) = self.cursor_motion(
1381 font_system,
1382 cursor,
1383 cursor_x_opt,
1384 Motion::LayoutCursor(layout_cursor),
1385 )?;
1386 }
1387 }
1388 Some((cursor, cursor_x_opt))
1389 }
1390
1391 #[cfg(feature = "swash")]
1393 pub fn draw<F>(
1394 &self,
1395 font_system: &mut FontSystem,
1396 cache: &mut crate::SwashCache,
1397 color: Color,
1398 callback: F,
1399 ) where
1400 F: FnMut(i32, i32, u32, u32, Color),
1401 {
1402 let mut renderer = crate::LegacyRenderer {
1403 font_system,
1404 cache,
1405 callback,
1406 };
1407 self.render(&mut renderer, color);
1408 }
1409
1410 pub fn render<R: Renderer>(&self, renderer: &mut R, color: Color) {
1411 for run in self.layout_runs() {
1412 for glyph in run.glyphs {
1413 let physical_glyph = glyph.physical((0., run.line_y), 1.0);
1414 let glyph_color = glyph.color_opt.map_or(color, |some| some);
1415 renderer.glyph(physical_glyph, glyph_color);
1416 }
1417 render_decoration(renderer, &run, color);
1419 }
1420 }
1421}
1422
1423impl BorrowedWithFontSystem<'_, Buffer> {
1424 pub fn shape_until_cursor(&mut self, cursor: Cursor, prune: bool) {
1426 self.inner
1427 .shape_until_cursor(self.font_system, cursor, prune);
1428 }
1429
1430 pub fn shape_until_scroll(&mut self, prune: bool) {
1432 self.inner.shape_until_scroll(self.font_system, prune);
1433 }
1434
1435 pub fn line_shape(&mut self, line_i: usize) -> Option<&ShapeLine> {
1437 self.inner.line_shape(self.font_system, line_i)
1438 }
1439
1440 pub fn line_layout(&mut self, line_i: usize) -> Option<&[LayoutLine]> {
1442 self.inner.line_layout(self.font_system, line_i)
1443 }
1444
1445 pub fn set_metrics(&mut self, metrics: Metrics) {
1451 self.inner.set_metrics(self.font_system, metrics);
1452 }
1453
1454 pub fn set_wrap(&mut self, wrap: Wrap) {
1456 self.inner.set_wrap(self.font_system, wrap);
1457 }
1458
1459 pub fn set_ellipsize(&mut self, ellipsize: Ellipsize) {
1461 self.inner.set_ellipsize(self.font_system, ellipsize);
1462 }
1463
1464 pub fn set_size(&mut self, width_opt: Option<f32>, height_opt: Option<f32>) {
1466 self.inner.set_size(self.font_system, width_opt, height_opt);
1467 }
1468
1469 pub fn set_metrics_and_size(
1475 &mut self,
1476 metrics: Metrics,
1477 width_opt: Option<f32>,
1478 height_opt: Option<f32>,
1479 ) {
1480 self.inner
1481 .set_metrics_and_size(self.font_system, metrics, width_opt, height_opt);
1482 }
1483
1484 pub fn set_tab_width(&mut self, tab_width: u16) {
1486 self.inner.set_tab_width(self.font_system, tab_width);
1487 }
1488
1489 pub fn set_text(
1491 &mut self,
1492 text: &str,
1493 attrs: &Attrs,
1494 shaping: Shaping,
1495 alignment: Option<Align>,
1496 ) {
1497 self.inner
1498 .set_text(self.font_system, text, attrs, shaping, alignment);
1499 }
1500
1501 pub fn set_rich_text<'r, 's, I>(
1520 &mut self,
1521 spans: I,
1522 default_attrs: &Attrs,
1523 shaping: Shaping,
1524 alignment: Option<Align>,
1525 ) where
1526 I: IntoIterator<Item = (&'s str, Attrs<'r>)>,
1527 {
1528 self.inner
1529 .set_rich_text(self.font_system, spans, default_attrs, shaping, alignment);
1530 }
1531
1532 pub fn cursor_motion(
1534 &mut self,
1535 cursor: Cursor,
1536 cursor_x_opt: Option<i32>,
1537 motion: Motion,
1538 ) -> Option<(Cursor, Option<i32>)> {
1539 self.inner
1540 .cursor_motion(self.font_system, cursor, cursor_x_opt, motion)
1541 }
1542
1543 #[cfg(feature = "swash")]
1545 pub fn draw<F>(&mut self, cache: &mut crate::SwashCache, color: Color, f: F)
1546 where
1547 F: FnMut(i32, i32, u32, u32, Color),
1548 {
1549 self.inner.draw(self.font_system, cache, color, f);
1550 }
1551}