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, Direction, Ellipsize, FontSystem, Hinting,
15 LayoutCursor, LayoutGlyph, LayoutLine, LineEnding, LineIter, Motion, Renderer, Scroll,
16 ShapeLine, Shaping, Wrap,
17};
18
19bitflags::bitflags! {
20 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
22 struct DirtyFlags: u8 {
23 const RELAYOUT = 0b0001;
25 const TAB_SHAPE = 0b0010;
27 const TEXT_SET = 0b0100;
29 const SCROLL = 0b1000;
31 const DIRECTION = 0b1_0000;
33 }
34}
35
36#[derive(Debug)]
38pub struct LayoutRun<'a> {
39 pub line_i: usize,
41 pub text: &'a str,
43 pub rtl: bool,
45 pub glyphs: &'a [LayoutGlyph],
47 pub decorations: &'a [DecorationSpan],
49 pub line_y: f32,
51 pub line_top: f32,
53 pub line_height: f32,
55 pub line_w: f32,
57}
58
59impl LayoutRun<'_> {
60 pub fn highlight(
69 &self,
70 cursor_start: Cursor,
71 cursor_end: Cursor,
72 ) -> impl Iterator<Item = (f32, f32)> {
73 let line_i = self.line_i;
74 let mut results = Vec::new();
75 let mut range_opt: Option<(f32, f32)> = None;
76
77 for glyph in self.glyphs {
78 let cluster = &self.text[glyph.start..glyph.end];
79 let total = cluster.grapheme_indices(true).count().max(1);
80 let c_w = glyph.w / total as f32;
81 let mut c_x = glyph.x;
82
83 for (i, c) in cluster.grapheme_indices(true) {
84 let c_start = glyph.start + i;
85 let c_end = glyph.start + i + c.len();
86
87 let is_selected = (cursor_start.line != line_i || c_end > cursor_start.index)
88 && (cursor_end.line != line_i || c_start < cursor_end.index);
89
90 if is_selected {
91 range_opt = Some(match range_opt {
92 Some((min, max)) => (min.min(c_x), max.max(c_x + c_w)),
93 None => (c_x, c_x + c_w),
94 });
95 } else if let Some((min_x, max_x)) = range_opt.take() {
96 let width = max_x - min_x;
97 if width > 0.0 {
98 results.push((min_x, width));
99 }
100 }
101
102 c_x += c_w;
103 }
104 }
105
106 if let Some((min_x, max_x)) = range_opt {
108 let width = max_x - min_x;
109 if width > 0.0 {
110 results.push((min_x, width));
111 }
112 }
113
114 results.into_iter()
115 }
116
117 pub fn cursor_position(&self, cursor: &Cursor) -> Option<f32> {
123 let (glyph_idx, glyph_offset) = self.cursor_glyph(cursor)?;
124 let x = self.glyphs.get(glyph_idx).map_or_else(
125 || {
126 self.glyphs.last().map_or(0.0, |glyph| {
128 if glyph.level.is_rtl() {
129 glyph.x
130 } else {
131 glyph.x + glyph.w
132 }
133 })
134 },
135 |glyph| {
136 if glyph.level.is_rtl() {
137 glyph.x + glyph.w - glyph_offset
138 } else {
139 glyph.x + glyph_offset
140 }
141 },
142 );
143 Some(x)
144 }
145
146 pub fn cursor_glyph(&self, cursor: &Cursor) -> Option<(usize, f32)> {
150 if cursor.line != self.line_i {
151 return None;
152 }
153 for (glyph_i, glyph) in self.glyphs.iter().enumerate() {
154 if cursor.index == glyph.start {
155 return Some((glyph_i, 0.0));
156 } else if cursor.index > glyph.start && cursor.index < glyph.end {
157 let cluster = &self.text[glyph.start..glyph.end];
159 let mut before = 0;
160 let mut total = 0;
161 for (i, _) in cluster.grapheme_indices(true) {
162 if glyph.start + i < cursor.index {
163 before += 1;
164 }
165 total += 1;
166 }
167 let offset = glyph.w * (before as f32) / (total as f32);
168 return Some((glyph_i, offset));
169 }
170 }
171 for (glyph_i, glyph) in self.glyphs.iter().enumerate() {
173 if cursor.index == glyph.end {
174 return Some((glyph_i, glyph.w));
175 }
176 }
177 if self.glyphs.is_empty() {
178 return Some((0, 0.0));
179 }
180 None
181 }
182
183 pub const fn cursor_from_glyph_left(&self, glyph: &LayoutGlyph) -> Cursor {
185 if self.rtl {
186 Cursor::new_with_affinity(self.line_i, glyph.end, Affinity::Before)
187 } else {
188 Cursor::new_with_affinity(self.line_i, glyph.start, Affinity::After)
189 }
190 }
191
192 pub const fn cursor_from_glyph_right(&self, glyph: &LayoutGlyph) -> Cursor {
194 if self.rtl {
195 Cursor::new_with_affinity(self.line_i, glyph.start, Affinity::After)
196 } else {
197 Cursor::new_with_affinity(self.line_i, glyph.end, Affinity::Before)
198 }
199 }
200}
201
202#[derive(Debug)]
204pub struct LayoutRunIter<'b> {
205 lines: &'b [BufferLine],
206 height_opt: Option<f32>,
207 line_height: f32,
208 scroll: f32,
209 line_i: usize,
210 layout_i: usize,
211 total_height: f32,
212 line_top: f32,
213}
214
215impl<'b> LayoutRunIter<'b> {
216 pub const fn new(buffer: &'b Buffer) -> Self {
217 Self::from_lines(
218 buffer.lines.as_slice(),
219 buffer.height_opt,
220 buffer.metrics.line_height,
221 buffer.scroll.vertical,
222 buffer.scroll.line,
223 )
224 }
225
226 pub const fn from_lines(
227 lines: &'b [BufferLine],
228 height_opt: Option<f32>,
229 line_height: f32,
230 scroll: f32,
231 start: usize,
232 ) -> Self {
233 Self {
234 lines,
235 height_opt,
236 line_height,
237 scroll,
238 line_i: start,
239 layout_i: 0,
240 total_height: 0.0,
241 line_top: 0.0,
242 }
243 }
244}
245
246impl<'b> Iterator for LayoutRunIter<'b> {
247 type Item = LayoutRun<'b>;
248
249 fn next(&mut self) -> Option<Self::Item> {
250 while let Some(line) = self.lines.get(self.line_i) {
251 let shape = line.shape_opt()?;
252 let layout = line.layout_opt()?;
253 while let Some(layout_line) = layout.get(self.layout_i) {
254 self.layout_i += 1;
255
256 let line_height = layout_line.line_height_opt.unwrap_or(self.line_height);
257 self.total_height += line_height;
258
259 let line_top = self.line_top - self.scroll;
260 let glyph_height = layout_line.max_ascent + layout_line.max_descent;
261 let centering_offset = (line_height - glyph_height) / 2.0;
262 let line_y = line_top + centering_offset + layout_line.max_ascent;
263 if let Some(height) = self.height_opt {
264 if line_y - layout_line.max_ascent > height {
265 return None;
266 }
267 }
268 self.line_top += line_height;
269 if line_y + layout_line.max_descent < 0.0 {
270 continue;
271 }
272
273 return Some(LayoutRun {
274 line_i: self.line_i,
275 text: line.text(),
276 rtl: shape.rtl,
277 glyphs: &layout_line.glyphs,
278 decorations: &layout_line.decorations,
279 line_y,
280 line_top,
281 line_height,
282 line_w: layout_line.w,
283 });
284 }
285 self.line_i += 1;
286 self.layout_i = 0;
287 }
288
289 None
290 }
291}
292
293#[derive(Clone, Copy, Debug, Default, PartialEq)]
295pub struct Metrics {
296 pub font_size: f32,
298 pub line_height: f32,
300}
301
302impl Metrics {
303 pub const fn new(font_size: f32, line_height: f32) -> Self {
305 Self {
306 font_size,
307 line_height,
308 }
309 }
310
311 pub fn relative(font_size: f32, line_height_scale: f32) -> Self {
313 Self {
314 font_size,
315 line_height: font_size * line_height_scale,
316 }
317 }
318
319 pub fn scale(self, scale: f32) -> Self {
321 Self {
322 font_size: self.font_size * scale,
323 line_height: self.line_height * scale,
324 }
325 }
326}
327
328impl fmt::Display for Metrics {
329 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
330 write!(f, "{}px / {}px", self.font_size, self.line_height)
331 }
332}
333
334#[derive(Debug)]
336pub struct Buffer {
337 pub lines: Vec<BufferLine>,
339 metrics: Metrics,
340 width_opt: Option<f32>,
341 height_opt: Option<f32>,
342 scroll: Scroll,
343 redraw: bool,
345 wrap: Wrap,
346 ellipsize: Ellipsize,
347 monospace_width: Option<f32>,
348 tab_width: u16,
349 hinting: Hinting,
350 direction: Direction,
351 dirty: DirtyFlags,
353}
354
355impl Clone for Buffer {
356 fn clone(&self) -> Self {
357 Self {
358 lines: self.lines.clone(),
359 metrics: self.metrics,
360 width_opt: self.width_opt,
361 height_opt: self.height_opt,
362 scroll: self.scroll,
363 redraw: self.redraw,
364 wrap: self.wrap,
365 ellipsize: self.ellipsize,
366 monospace_width: self.monospace_width,
367 tab_width: self.tab_width,
368 hinting: self.hinting,
369 direction: self.direction,
370 dirty: self.dirty,
371 }
372 }
373}
374
375impl Buffer {
376 pub fn new_empty(metrics: Metrics) -> Self {
388 assert_ne!(metrics.line_height, 0.0, "line height cannot be 0");
389 Self {
390 lines: Vec::new(),
391 metrics,
392 width_opt: None,
393 height_opt: None,
394 scroll: Scroll::default(),
395 redraw: false,
396 wrap: Wrap::WordOrGlyph,
397 ellipsize: Ellipsize::None,
398 monospace_width: None,
399 tab_width: 8,
400 hinting: Hinting::default(),
401 direction: Direction::default(),
402 dirty: DirtyFlags::empty(),
403 }
404 }
405
406 pub fn new(font_system: &mut FontSystem, metrics: Metrics) -> Self {
412 let mut buffer = Self::new_empty(metrics);
413 buffer.set_text("", &Attrs::new(), Shaping::Advanced, None);
414 buffer.shape_until_scroll(font_system, false);
415 buffer
416 }
417
418 pub fn borrow_with<'a>(
420 &'a mut self,
421 font_system: &'a mut FontSystem,
422 ) -> BorrowedWithFontSystem<'a, Self> {
423 BorrowedWithFontSystem {
424 inner: self,
425 font_system,
426 }
427 }
428
429 fn resolve_dirty(&mut self) -> bool {
432 let dirty = self.dirty;
433 if dirty.is_empty() {
434 if self.lines.iter().any(|line| line.needs_reshaping()) {
436 self.redraw = true;
437 return true;
438 }
439 return false;
440 }
441
442 if dirty.contains(DirtyFlags::TEXT_SET) {
443 } else {
445 if dirty.contains(DirtyFlags::DIRECTION) {
446 for line in &mut self.lines {
447 if line.shape_opt().is_some() {
448 line.reset_shaping();
449 }
450 }
451 } else if dirty.contains(DirtyFlags::TAB_SHAPE) {
452 for line in &mut self.lines {
453 if line.shape_opt().is_some() && line.text().contains('\t') {
454 line.reset_shaping();
455 }
456 }
457 }
458 if dirty.contains(DirtyFlags::RELAYOUT) {
459 for line in &mut self.lines {
460 if line.shape_opt().is_some() {
461 line.reset_layout();
462 }
463 }
464 }
465 }
466
467 self.redraw = true;
468 self.dirty = DirtyFlags::empty();
469 true
470 }
471
472 #[allow(clippy::missing_panics_doc)]
474 pub fn shape_until_cursor(
475 &mut self,
476 font_system: &mut FontSystem,
477 cursor: Cursor,
478 prune: bool,
479 ) {
480 self.shape_until_scroll(font_system, prune);
481 let metrics = self.metrics;
482 let old_scroll = self.scroll;
483
484 let layout_cursor = self
485 .layout_cursor(font_system, cursor)
486 .expect("shape_until_cursor invalid cursor");
487
488 let mut layout_y = 0.0;
489 let mut total_height = {
490 let layout = self
491 .line_layout(font_system, layout_cursor.line)
492 .expect("shape_until_cursor failed to scroll forwards");
493 (0..layout_cursor.layout).for_each(|layout_i| {
494 layout_y += layout[layout_i]
495 .line_height_opt
496 .unwrap_or(metrics.line_height);
497 });
498 layout_y
499 + layout[layout_cursor.layout]
500 .line_height_opt
501 .unwrap_or(metrics.line_height)
502 };
503
504 if self.scroll.line > layout_cursor.line
505 || (self.scroll.line == layout_cursor.line && self.scroll.vertical > layout_y)
506 {
507 self.scroll.line = layout_cursor.line;
509 self.scroll.vertical = layout_y;
510 } else if let Some(height) = self.height_opt {
511 let mut line_i = layout_cursor.line;
513 if line_i <= self.scroll.line {
514 if total_height > height + self.scroll.vertical {
516 self.scroll.vertical = total_height - height;
517 }
518 } else {
519 while line_i > self.scroll.line {
520 line_i -= 1;
521 let layout = self
522 .line_layout(font_system, line_i)
523 .expect("shape_until_cursor failed to scroll forwards");
524 for layout_line in layout {
525 total_height += layout_line.line_height_opt.unwrap_or(metrics.line_height);
526 }
527 if total_height > height + self.scroll.vertical {
528 self.scroll.line = line_i;
529 self.scroll.vertical = total_height - height;
530 }
531 }
532 }
533 }
534
535 if old_scroll != self.scroll {
536 self.dirty |= DirtyFlags::SCROLL;
537 }
538
539 self.shape_until_scroll(font_system, prune);
540
541 if let Some(layout_cursor) = self.layout_cursor(font_system, cursor) {
543 if let Some(layout_lines) = self.line_layout(font_system, layout_cursor.line) {
544 if let Some(layout_line) = layout_lines.get(layout_cursor.layout) {
545 let (x_min, x_max) = layout_line
546 .glyphs
547 .get(layout_cursor.glyph)
548 .or_else(|| layout_line.glyphs.last())
549 .map_or((0.0, 0.0), |glyph| {
550 let x_a = glyph.x;
552 let x_b = glyph.x + glyph.w;
553 (x_a.min(x_b), x_a.max(x_b))
554 });
555 if x_min < self.scroll.horizontal {
556 self.scroll.horizontal = x_min;
557 self.redraw = true;
558 }
559 if let Some(width) = self.width_opt {
560 if x_max > self.scroll.horizontal + width {
561 self.scroll.horizontal = x_max - width;
562 self.redraw = true;
563 }
564 }
565 }
566 }
567 }
568 }
569
570 #[allow(clippy::missing_panics_doc)]
582 pub fn shape_until_scroll(&mut self, font_system: &mut FontSystem, prune: bool) {
583 if !self.resolve_dirty() {
584 return;
585 }
586 let metrics = self.metrics;
587
588 if self.scroll.line >= self.lines.len() {
590 self.scroll.line = self.lines.len().saturating_sub(1);
591 self.scroll.vertical = 0.0;
592 }
593
594 let old_scroll = self.scroll;
595
596 loop {
597 while self.scroll.vertical < 0.0 {
599 if self.scroll.line > 0 {
600 let line_i = self.scroll.line - 1;
601 if let Some(layout) = self.line_layout(font_system, line_i) {
602 let mut layout_height = 0.0;
603 for layout_line in layout {
604 layout_height +=
605 layout_line.line_height_opt.unwrap_or(metrics.line_height);
606 }
607 self.scroll.line = line_i;
608 self.scroll.vertical += layout_height;
609 } else {
610 self.scroll.line = line_i;
612 self.scroll.vertical += metrics.line_height;
613 }
614 } else {
615 self.scroll.vertical = 0.0;
616 break;
617 }
618 }
619
620 let scroll_start = self.scroll.vertical;
621 let scroll_end = scroll_start + self.height_opt.unwrap_or(f32::INFINITY);
622
623 if prune {
624 for line_i in 0..self.scroll.line {
625 self.lines[line_i].reset_shaping();
626 }
627 }
628 let mut total_height = 0.0;
629 for line_i in self.scroll.line..self.lines.len() {
630 if total_height > scroll_end {
631 if prune {
632 self.lines[line_i].reset_shaping();
633 continue;
634 }
635 break;
636 }
637
638 let mut layout_height = 0.0;
639 let layout = self
640 .line_layout(font_system, line_i)
641 .expect("shape_until_scroll invalid line");
642 for layout_line in layout {
643 let line_height = layout_line.line_height_opt.unwrap_or(metrics.line_height);
644 layout_height += line_height;
645 total_height += line_height;
646 }
647
648 if line_i == self.scroll.line && layout_height <= self.scroll.vertical {
650 self.scroll.line += 1;
651 self.scroll.vertical -= layout_height;
652 }
653 }
654
655 if total_height < scroll_end && self.scroll.line > 0 {
656 self.scroll.vertical -= scroll_end - total_height;
658 } else {
659 break;
661 }
662 }
663
664 if old_scroll != self.scroll {
665 self.redraw = true;
666 }
667 }
668
669 pub fn layout_cursor(
671 &mut self,
672 font_system: &mut FontSystem,
673 cursor: Cursor,
674 ) -> Option<LayoutCursor> {
675 let layout = self.line_layout(font_system, cursor.line)?;
676 for (layout_i, layout_line) in layout.iter().enumerate() {
677 for (glyph_i, glyph) in layout_line.glyphs.iter().enumerate() {
678 let cursor_end =
679 Cursor::new_with_affinity(cursor.line, glyph.end, Affinity::Before);
680 let cursor_start =
681 Cursor::new_with_affinity(cursor.line, glyph.start, Affinity::After);
682 let (cursor_left, cursor_right) = if glyph.level.is_ltr() {
683 (cursor_start, cursor_end)
684 } else {
685 (cursor_end, cursor_start)
686 };
687 if cursor == cursor_left {
688 return Some(LayoutCursor::new(cursor.line, layout_i, glyph_i));
689 }
690 if cursor == cursor_right {
691 return Some(LayoutCursor::new(cursor.line, layout_i, glyph_i + 1));
692 }
693 }
694 }
695
696 Some(LayoutCursor::new(cursor.line, 0, 0))
699 }
700
701 pub fn line_shape(
703 &mut self,
704 font_system: &mut FontSystem,
705 line_i: usize,
706 ) -> Option<&ShapeLine> {
707 let line = self.lines.get_mut(line_i)?;
708 Some(line.shape(font_system, self.tab_width, self.direction))
709 }
710
711 pub fn line_layout(
713 &mut self,
714 font_system: &mut FontSystem,
715 line_i: usize,
716 ) -> Option<&[LayoutLine]> {
717 let line = self.lines.get_mut(line_i)?;
718 Some(line.layout(
719 font_system,
720 self.metrics.font_size,
721 self.width_opt,
722 self.wrap,
723 self.ellipsize,
724 self.monospace_width,
725 self.tab_width,
726 self.hinting,
727 self.direction,
728 ))
729 }
730
731 pub const fn metrics(&self) -> Metrics {
733 self.metrics
734 }
735
736 pub fn set_metrics(&mut self, metrics: Metrics) {
742 if metrics != self.metrics {
743 assert_ne!(metrics.font_size, 0.0, "font size cannot be 0");
744 assert_ne!(metrics.line_height, 0.0, "line height cannot be 0");
745 self.metrics = metrics;
746 self.dirty |= DirtyFlags::RELAYOUT;
747 self.redraw = true;
748 }
749 }
750
751 pub const fn hinting(&self) -> Hinting {
753 self.hinting
754 }
755
756 pub fn set_hinting(&mut self, hinting: Hinting) {
758 if hinting != self.hinting {
759 self.hinting = hinting;
760 self.dirty |= DirtyFlags::RELAYOUT;
761 self.redraw = true;
762 }
763 }
764
765 pub const fn wrap(&self) -> Wrap {
767 self.wrap
768 }
769
770 pub fn set_wrap(&mut self, wrap: Wrap) {
772 if wrap != self.wrap {
773 self.wrap = wrap;
774 self.dirty |= DirtyFlags::RELAYOUT;
775 self.redraw = true;
776 }
777 }
778
779 pub const fn ellipsize(&self) -> Ellipsize {
781 self.ellipsize
782 }
783
784 pub fn set_ellipsize(&mut self, ellipsize: Ellipsize) {
786 if ellipsize != self.ellipsize {
787 self.ellipsize = ellipsize;
788 self.dirty |= DirtyFlags::RELAYOUT;
789 self.redraw = true;
790 }
791 }
792
793 pub const fn monospace_width(&self) -> Option<f32> {
795 self.monospace_width
796 }
797
798 pub fn set_monospace_width(&mut self, monospace_width: Option<f32>) {
800 if monospace_width != self.monospace_width {
801 self.monospace_width = monospace_width;
802 self.dirty |= DirtyFlags::RELAYOUT;
803 self.redraw = true;
804 }
805 }
806
807 pub const fn tab_width(&self) -> u16 {
809 self.tab_width
810 }
811
812 pub fn set_tab_width(&mut self, tab_width: u16) {
814 if tab_width == 0 {
815 return;
816 }
817 if tab_width != self.tab_width {
818 self.tab_width = tab_width;
819 self.dirty |= DirtyFlags::TAB_SHAPE | DirtyFlags::RELAYOUT;
820 self.redraw = true;
821 }
822 }
823
824 pub const fn direction(&self) -> Direction {
826 self.direction
827 }
828
829 pub fn set_direction(&mut self, direction: Direction) {
836 if direction != self.direction {
837 self.direction = direction;
838 self.dirty |= DirtyFlags::DIRECTION;
840 self.redraw = true;
841 }
842 }
843
844 pub const fn size(&self) -> (Option<f32>, Option<f32>) {
846 (self.width_opt, self.height_opt)
847 }
848
849 pub fn set_size(&mut self, width_opt: Option<f32>, height_opt: Option<f32>) {
851 let width_clamped = width_opt.map(|v| v.max(0.0));
852 let height_clamped = height_opt.map(|v| v.max(0.0));
853 if width_clamped != self.width_opt {
854 self.width_opt = width_clamped;
855 self.dirty |= DirtyFlags::RELAYOUT;
856 self.redraw = true;
857 }
858 if height_clamped != self.height_opt {
859 self.height_opt = height_clamped;
860 self.dirty |= DirtyFlags::RELAYOUT;
861 self.redraw = true;
862 }
863 }
864
865 pub fn set_metrics_and_size(
871 &mut self,
872 metrics: Metrics,
873 width_opt: Option<f32>,
874 height_opt: Option<f32>,
875 ) {
876 self.set_metrics(metrics);
877 self.set_size(width_opt, height_opt);
878 }
879
880 pub const fn scroll(&self) -> Scroll {
882 self.scroll
883 }
884
885 pub fn set_scroll(&mut self, scroll: Scroll) {
887 if scroll != self.scroll {
888 self.scroll = scroll;
889 self.dirty |= DirtyFlags::SCROLL;
890 self.redraw = true;
891 }
892 }
893
894 fn set_text_impl(
898 &mut self,
899 text: &str,
900 attrs: &Attrs,
901 shaping: Shaping,
902 alignment: Option<Align>,
903 ) {
904 let mut line_count = 0;
905 for (range, ending) in LineIter::new(text) {
906 let line_text = &text[range];
907 if line_count < self.lines.len() {
908 let mut reused_text = self.lines[line_count].reclaim_text();
910 reused_text.push_str(line_text);
911 let reused_attrs = self.lines[line_count].reclaim_attrs().reset(attrs);
912 self.lines[line_count].reset_new(reused_text, ending, reused_attrs, shaping);
913 } else {
914 self.lines.push(BufferLine::new(
915 line_text,
916 ending,
917 AttrsList::new(attrs),
918 shaping,
919 ));
920 }
921 line_count += 1;
922 }
923
924 let last_ending = if line_count > 0 {
928 self.lines[line_count - 1].ending()
929 } else {
930 LineEnding::default()
931 };
932 if last_ending != LineEnding::None {
933 if line_count < self.lines.len() {
934 let reused_text = self.lines[line_count].reclaim_text();
935 let reused_attrs = self.lines[line_count].reclaim_attrs().reset(attrs);
936 self.lines[line_count].reset_new(
937 reused_text,
938 LineEnding::None,
939 reused_attrs,
940 shaping,
941 );
942 } else {
943 self.lines.push(BufferLine::new(
944 "",
945 LineEnding::None,
946 AttrsList::new(attrs),
947 shaping,
948 ));
949 }
950 line_count += 1;
951 }
952
953 self.lines.truncate(line_count);
955
956 if alignment.is_some() {
957 self.lines.iter_mut().for_each(|line| {
958 line.set_align(alignment);
959 });
960 }
961
962 self.scroll = Scroll::default();
963 }
964
965 pub fn set_text(
967 &mut self,
968 text: &str,
969 attrs: &Attrs,
970 shaping: Shaping,
971 alignment: Option<Align>,
972 ) {
973 self.set_text_impl(text, attrs, shaping, alignment);
974 self.dirty |= DirtyFlags::TEXT_SET;
975 self.redraw = true;
976 }
977
978 fn set_rich_text_impl<'r, 's, I>(
982 &mut self,
983 spans: I,
984 default_attrs: &Attrs,
985 shaping: Shaping,
986 alignment: Option<Align>,
987 ) where
988 I: IntoIterator<Item = (&'s str, Attrs<'r>)>,
989 {
990 let mut end = 0;
991 let (string, spans_data): (String, Vec<_>) = spans
993 .into_iter()
994 .map(|(s, attrs)| {
995 let start = end;
996 end += s.len();
997 (s, (attrs, start..end))
998 })
999 .unzip();
1000
1001 let mut spans_iter = spans_data.into_iter();
1002 let mut maybe_span = spans_iter.next();
1003
1004 let string_start = string.as_ptr() as usize;
1006 let mut lines_iter = BidiParagraphs::new(&string).map(|line: &str| {
1007 let start = line.as_ptr() as usize - string_start;
1008 let end = start + line.len();
1009 start..end
1010 });
1011 let mut maybe_line = lines_iter.next();
1012 let line_ending = LineEnding::default();
1014
1015 let mut line_count = 0;
1016 let mut attrs_list = self
1017 .lines
1018 .get_mut(line_count)
1019 .map_or_else(|| AttrsList::new(&Attrs::new()), BufferLine::reclaim_attrs)
1020 .reset(default_attrs);
1021 let mut line_string = self
1022 .lines
1023 .get_mut(line_count)
1024 .map(BufferLine::reclaim_text)
1025 .unwrap_or_default();
1026
1027 loop {
1028 let (Some(line_range), Some((attrs, span_range))) = (&maybe_line, &maybe_span) else {
1029 if self.lines.len() == line_count {
1031 self.lines.push(BufferLine::empty());
1032 }
1033 self.lines[line_count].reset_new(
1034 String::new(),
1035 line_ending,
1036 AttrsList::new(default_attrs),
1037 shaping,
1038 );
1039 line_count += 1;
1040 break;
1041 };
1042
1043 let start = line_range.start.max(span_range.start);
1045 let end = line_range.end.min(span_range.end);
1046 if start < end {
1047 let text = &string[start..end];
1048 let text_start = line_string.len();
1049 line_string.push_str(text);
1050 let text_end = line_string.len();
1051 if *attrs != attrs_list.defaults() {
1053 attrs_list.add_span(text_start..text_end, attrs);
1054 }
1055 } else if line_string.is_empty() && attrs.metrics_opt.is_some() {
1056 attrs_list = attrs_list.reset(attrs);
1060 }
1061
1062 if span_range.end < line_range.end {
1068 maybe_span = spans_iter.next();
1069 } else {
1070 maybe_line = lines_iter.next();
1071 if maybe_line.is_some() {
1072 let next_attrs_list = self
1074 .lines
1075 .get_mut(line_count + 1)
1076 .map_or_else(|| AttrsList::new(&Attrs::new()), BufferLine::reclaim_attrs)
1077 .reset(default_attrs);
1078 let next_line_string = self
1079 .lines
1080 .get_mut(line_count + 1)
1081 .map(BufferLine::reclaim_text)
1082 .unwrap_or_default();
1083 let prev_attrs_list = core::mem::replace(&mut attrs_list, next_attrs_list);
1084 let prev_line_string = core::mem::replace(&mut line_string, next_line_string);
1085 if self.lines.len() == line_count {
1086 self.lines.push(BufferLine::empty());
1087 }
1088 self.lines[line_count].reset_new(
1089 prev_line_string,
1090 line_ending,
1091 prev_attrs_list,
1092 shaping,
1093 );
1094 line_count += 1;
1095 } else {
1096 if self.lines.len() == line_count {
1098 self.lines.push(BufferLine::empty());
1099 }
1100 self.lines[line_count].reset_new(line_string, line_ending, attrs_list, shaping);
1101 line_count += 1;
1102 break;
1103 }
1104 }
1105 }
1106
1107 self.lines.truncate(line_count);
1109
1110 self.lines.iter_mut().for_each(|line| {
1111 line.set_align(alignment);
1112 });
1113
1114 self.scroll = Scroll::default();
1115 }
1116
1117 pub fn set_rich_text<'r, 's, I>(
1135 &mut self,
1136 spans: I,
1137 default_attrs: &Attrs,
1138 shaping: Shaping,
1139 alignment: Option<Align>,
1140 ) where
1141 I: IntoIterator<Item = (&'s str, Attrs<'r>)>,
1142 {
1143 self.set_rich_text_impl(spans, default_attrs, shaping, alignment);
1144 self.dirty |= DirtyFlags::TEXT_SET;
1145 self.redraw = true;
1146 }
1147
1148 pub const fn redraw(&self) -> bool {
1150 self.redraw
1151 }
1152
1153 pub fn set_redraw(&mut self, redraw: bool) {
1155 self.redraw = redraw;
1156 }
1157
1158 pub fn layout_runs(&self) -> LayoutRunIter<'_> {
1167 LayoutRunIter::new(self)
1168 }
1169
1170 pub fn hit(&self, x: f32, y: f32) -> Option<Cursor> {
1177 #[cfg(all(feature = "std", not(target_arch = "wasm32")))]
1178 let instant = std::time::Instant::now();
1179
1180 let mut new_cursor_opt = None;
1181
1182 let mut runs = self.layout_runs().peekable();
1183 let mut first_run = true;
1184 while let Some(run) = runs.next() {
1185 let line_top = run.line_top;
1186 let line_height = run.line_height;
1187
1188 if first_run && y < line_top {
1189 first_run = false;
1190 let new_cursor = Cursor::new(run.line_i, 0);
1191 new_cursor_opt = Some(new_cursor);
1192 } else if y >= line_top && y < line_top + line_height {
1193 let mut new_cursor_glyph = run.glyphs.len();
1194 let mut new_cursor_char = 0;
1195 let mut new_cursor_affinity = Affinity::After;
1196
1197 let mut first_glyph = true;
1198
1199 'hit: for (glyph_i, glyph) in run.glyphs.iter().enumerate() {
1200 if first_glyph {
1201 first_glyph = false;
1202 if (run.rtl && x > glyph.x) || (!run.rtl && x < 0.0) {
1203 new_cursor_glyph = 0;
1204 new_cursor_char = 0;
1205 }
1206 }
1207 if x >= glyph.x && x <= glyph.x + glyph.w {
1208 new_cursor_glyph = glyph_i;
1209
1210 let cluster = &run.text[glyph.start..glyph.end];
1211 let total = cluster.grapheme_indices(true).count();
1212 let mut egc_x = glyph.x;
1213 let egc_w = glyph.w / (total as f32);
1214 for (egc_i, egc) in cluster.grapheme_indices(true) {
1215 if x >= egc_x && x <= egc_x + egc_w {
1216 new_cursor_char = egc_i;
1217
1218 let right_half = x >= egc_x + egc_w / 2.0;
1219 if right_half != glyph.level.is_rtl() {
1220 new_cursor_char += egc.len();
1222 new_cursor_affinity = Affinity::Before;
1223 }
1224 break 'hit;
1225 }
1226 egc_x += egc_w;
1227 }
1228
1229 let right_half = x >= glyph.x + glyph.w / 2.0;
1230 if right_half != glyph.level.is_rtl() {
1231 new_cursor_char = cluster.len();
1233 new_cursor_affinity = Affinity::Before;
1234 }
1235 break 'hit;
1236 }
1237 }
1238
1239 let mut new_cursor = Cursor::new(run.line_i, 0);
1240
1241 match run.glyphs.get(new_cursor_glyph) {
1242 Some(glyph) => {
1243 new_cursor.index = glyph.start + new_cursor_char;
1245 new_cursor.affinity = new_cursor_affinity;
1246 }
1247 None => {
1248 let run_end = run.glyphs.iter().map(|g| g.end).max().unwrap_or(0);
1253 new_cursor.index = run_end;
1254 new_cursor.affinity = Affinity::Before;
1255 }
1256 }
1257
1258 new_cursor_opt = Some(new_cursor);
1259
1260 break;
1261 } else if runs.peek().is_none() && y > run.line_y {
1262 let new_cursor =
1265 Cursor::new_with_affinity(run.line_i, run.text.len(), Affinity::Before);
1266 new_cursor_opt = Some(new_cursor);
1267 }
1268 }
1269
1270 #[cfg(all(feature = "std", not(target_arch = "wasm32")))]
1271 log::trace!("click({}, {}): {:?}", x, y, instant.elapsed());
1272
1273 new_cursor_opt
1274 }
1275
1276 pub fn cursor_position(&self, cursor: &Cursor) -> Option<(f32, f32)> {
1280 self.layout_runs()
1281 .filter(|run| run.line_i == cursor.line)
1282 .find_map(|run| run.cursor_position(cursor).map(|x| (x, run.line_top)))
1283 }
1284
1285 pub fn is_rtl(&self, line: usize) -> Option<bool> {
1288 self.lines.get(line)?.shape_opt().map(|shape| shape.rtl)
1289 }
1290
1291 pub fn cursor_motion(
1293 &mut self,
1294 font_system: &mut FontSystem,
1295 mut cursor: Cursor,
1296 mut cursor_x_opt: Option<i32>,
1297 motion: Motion,
1298 ) -> Option<(Cursor, Option<i32>)> {
1299 match motion {
1300 Motion::LayoutCursor(layout_cursor) => {
1301 let layout = self.line_layout(font_system, layout_cursor.line)?;
1302
1303 let layout_line = match layout.get(layout_cursor.layout) {
1304 Some(some) => some,
1305 None => match layout.last() {
1306 Some(some) => some,
1307 None => {
1308 return None;
1309 }
1310 },
1311 };
1312
1313 let (new_index, new_affinity) =
1314 layout_line.glyphs.get(layout_cursor.glyph).map_or_else(
1315 || {
1316 layout_line
1317 .glyphs
1318 .last()
1319 .map_or((0, Affinity::After), |glyph| (glyph.end, Affinity::Before))
1320 },
1321 |glyph| (glyph.start, Affinity::After),
1322 );
1323
1324 if cursor.line != layout_cursor.line
1325 || cursor.index != new_index
1326 || cursor.affinity != new_affinity
1327 {
1328 cursor.line = layout_cursor.line;
1329 cursor.index = new_index;
1330 cursor.affinity = new_affinity;
1331 }
1332 }
1333 Motion::Previous => {
1334 let line = self.lines.get(cursor.line)?;
1335 if cursor.index > 0 {
1336 let mut prev_index = 0;
1338 for (i, _) in line.text().grapheme_indices(true) {
1339 if i < cursor.index {
1340 prev_index = i;
1341 } else {
1342 break;
1343 }
1344 }
1345
1346 cursor.index = prev_index;
1347 cursor.affinity = Affinity::After;
1348 } else if cursor.line > 0 {
1349 cursor.line -= 1;
1350 cursor.index = self.lines.get(cursor.line)?.text().len();
1351 cursor.affinity = Affinity::After;
1352 }
1353 cursor_x_opt = None;
1354 }
1355 Motion::Next => {
1356 let line = self.lines.get(cursor.line)?;
1357 if cursor.index < line.text().len() {
1358 for (i, c) in line.text().grapheme_indices(true) {
1359 if i == cursor.index {
1360 cursor.index += c.len();
1361 cursor.affinity = Affinity::Before;
1362 break;
1363 }
1364 }
1365 } else if cursor.line + 1 < self.lines.len() {
1366 cursor.line += 1;
1367 cursor.index = 0;
1368 cursor.affinity = Affinity::Before;
1369 }
1370 cursor_x_opt = None;
1371 }
1372 Motion::Left => {
1373 let rtl_opt = self
1374 .line_shape(font_system, cursor.line)
1375 .map(|shape| shape.rtl);
1376 if let Some(rtl) = rtl_opt {
1377 if rtl {
1378 (cursor, cursor_x_opt) =
1379 self.cursor_motion(font_system, cursor, cursor_x_opt, Motion::Next)?;
1380 } else {
1381 (cursor, cursor_x_opt) = self.cursor_motion(
1382 font_system,
1383 cursor,
1384 cursor_x_opt,
1385 Motion::Previous,
1386 )?;
1387 }
1388 }
1389 }
1390 Motion::Right => {
1391 let rtl_opt = self
1392 .line_shape(font_system, cursor.line)
1393 .map(|shape| shape.rtl);
1394 if let Some(rtl) = rtl_opt {
1395 if rtl {
1396 (cursor, cursor_x_opt) = self.cursor_motion(
1397 font_system,
1398 cursor,
1399 cursor_x_opt,
1400 Motion::Previous,
1401 )?;
1402 } else {
1403 (cursor, cursor_x_opt) =
1404 self.cursor_motion(font_system, cursor, cursor_x_opt, Motion::Next)?;
1405 }
1406 }
1407 }
1408 Motion::Up => {
1409 let mut layout_cursor = self.layout_cursor(font_system, cursor)?;
1410
1411 if cursor_x_opt.is_none() {
1412 cursor_x_opt = Some(
1413 layout_cursor.glyph as i32, );
1415 }
1416
1417 if layout_cursor.layout > 0 {
1418 layout_cursor.layout -= 1;
1419 } else if layout_cursor.line > 0 {
1420 layout_cursor.line -= 1;
1421 layout_cursor.layout = usize::MAX;
1422 }
1423
1424 if let Some(cursor_x) = cursor_x_opt {
1425 layout_cursor.glyph = cursor_x as usize; }
1427
1428 (cursor, cursor_x_opt) = self.cursor_motion(
1429 font_system,
1430 cursor,
1431 cursor_x_opt,
1432 Motion::LayoutCursor(layout_cursor),
1433 )?;
1434 }
1435 Motion::Down => {
1436 let mut layout_cursor = self.layout_cursor(font_system, cursor)?;
1437
1438 let layout_len = self.line_layout(font_system, layout_cursor.line)?.len();
1439
1440 if cursor_x_opt.is_none() {
1441 cursor_x_opt = Some(
1442 layout_cursor.glyph as i32, );
1444 }
1445
1446 if layout_cursor.layout + 1 < layout_len {
1447 layout_cursor.layout += 1;
1448 } else if layout_cursor.line + 1 < self.lines.len() {
1449 layout_cursor.line += 1;
1450 layout_cursor.layout = 0;
1451 }
1452
1453 if let Some(cursor_x) = cursor_x_opt {
1454 layout_cursor.glyph = cursor_x as usize; }
1456
1457 (cursor, cursor_x_opt) = self.cursor_motion(
1458 font_system,
1459 cursor,
1460 cursor_x_opt,
1461 Motion::LayoutCursor(layout_cursor),
1462 )?;
1463 }
1464 Motion::Home => {
1465 cursor.index = 0;
1466 cursor_x_opt = None;
1467 }
1468 Motion::SoftHome => {
1469 let line = self.lines.get(cursor.line)?;
1470 cursor.index = line
1471 .text()
1472 .char_indices()
1473 .find_map(|(i, c)| if c.is_whitespace() { None } else { Some(i) })
1474 .unwrap_or(0);
1475 cursor_x_opt = None;
1476 }
1477 Motion::End => {
1478 let line = self.lines.get(cursor.line)?;
1479 cursor.index = line.text().len();
1480 cursor_x_opt = None;
1481 }
1482 Motion::ParagraphStart => {
1483 cursor.index = 0;
1484 cursor_x_opt = None;
1485 }
1486 Motion::ParagraphEnd => {
1487 cursor.index = self.lines.get(cursor.line)?.text().len();
1488 cursor_x_opt = None;
1489 }
1490 Motion::PageUp => {
1491 if let Some(height) = self.height_opt {
1492 (cursor, cursor_x_opt) = self.cursor_motion(
1493 font_system,
1494 cursor,
1495 cursor_x_opt,
1496 Motion::Vertical(-height as i32),
1497 )?;
1498 }
1499 }
1500 Motion::PageDown => {
1501 if let Some(height) = self.height_opt {
1502 (cursor, cursor_x_opt) = self.cursor_motion(
1503 font_system,
1504 cursor,
1505 cursor_x_opt,
1506 Motion::Vertical(height as i32),
1507 )?;
1508 }
1509 }
1510 Motion::Vertical(px) => {
1511 let lines = px / self.metrics().line_height as i32;
1513 match lines.cmp(&0) {
1514 cmp::Ordering::Less => {
1515 for _ in 0..-lines {
1516 (cursor, cursor_x_opt) =
1517 self.cursor_motion(font_system, cursor, cursor_x_opt, Motion::Up)?;
1518 }
1519 }
1520 cmp::Ordering::Greater => {
1521 for _ in 0..lines {
1522 (cursor, cursor_x_opt) = self.cursor_motion(
1523 font_system,
1524 cursor,
1525 cursor_x_opt,
1526 Motion::Down,
1527 )?;
1528 }
1529 }
1530 cmp::Ordering::Equal => {}
1531 }
1532 }
1533 Motion::PreviousWord => {
1534 let line = self.lines.get(cursor.line)?;
1535 if cursor.index > 0 {
1536 cursor.index = line
1537 .text()
1538 .unicode_word_indices()
1539 .rev()
1540 .map(|(i, _)| i)
1541 .find(|&i| i < cursor.index)
1542 .unwrap_or(0);
1543 } else if cursor.line > 0 {
1544 cursor.line -= 1;
1545 cursor.index = self.lines.get(cursor.line)?.text().len();
1546 }
1547 cursor_x_opt = None;
1548 }
1549 Motion::NextWord => {
1550 let line = self.lines.get(cursor.line)?;
1551 if cursor.index < line.text().len() {
1552 cursor.index = line
1553 .text()
1554 .unicode_word_indices()
1555 .map(|(i, word)| i + word.len())
1556 .find(|&i| i > cursor.index)
1557 .unwrap_or_else(|| line.text().len());
1558 } else if cursor.line + 1 < self.lines.len() {
1559 cursor.line += 1;
1560 cursor.index = 0;
1561 }
1562 cursor_x_opt = None;
1563 }
1564 Motion::LeftWord => {
1565 let rtl_opt = self
1566 .line_shape(font_system, cursor.line)
1567 .map(|shape| shape.rtl);
1568 if let Some(rtl) = rtl_opt {
1569 if rtl {
1570 (cursor, cursor_x_opt) = self.cursor_motion(
1571 font_system,
1572 cursor,
1573 cursor_x_opt,
1574 Motion::NextWord,
1575 )?;
1576 } else {
1577 (cursor, cursor_x_opt) = self.cursor_motion(
1578 font_system,
1579 cursor,
1580 cursor_x_opt,
1581 Motion::PreviousWord,
1582 )?;
1583 }
1584 }
1585 }
1586 Motion::RightWord => {
1587 let rtl_opt = self
1588 .line_shape(font_system, cursor.line)
1589 .map(|shape| shape.rtl);
1590 if let Some(rtl) = rtl_opt {
1591 if rtl {
1592 (cursor, cursor_x_opt) = self.cursor_motion(
1593 font_system,
1594 cursor,
1595 cursor_x_opt,
1596 Motion::PreviousWord,
1597 )?;
1598 } else {
1599 (cursor, cursor_x_opt) = self.cursor_motion(
1600 font_system,
1601 cursor,
1602 cursor_x_opt,
1603 Motion::NextWord,
1604 )?;
1605 }
1606 }
1607 }
1608 Motion::BufferStart => {
1609 cursor.line = 0;
1610 cursor.index = 0;
1611 cursor_x_opt = None;
1612 }
1613 Motion::BufferEnd => {
1614 cursor.line = self.lines.len().saturating_sub(1);
1615 cursor.index = self.lines.get(cursor.line)?.text().len();
1616 cursor_x_opt = None;
1617 }
1618 Motion::GotoLine(line) => {
1619 let mut layout_cursor = self.layout_cursor(font_system, cursor)?;
1620 layout_cursor.line = line;
1621 (cursor, cursor_x_opt) = self.cursor_motion(
1622 font_system,
1623 cursor,
1624 cursor_x_opt,
1625 Motion::LayoutCursor(layout_cursor),
1626 )?;
1627 }
1628 }
1629 Some((cursor, cursor_x_opt))
1630 }
1631
1632 #[cfg(feature = "swash")]
1636 pub fn draw<F>(
1637 &mut self,
1638 font_system: &mut FontSystem,
1639 cache: &mut crate::SwashCache,
1640 color: Color,
1641 callback: F,
1642 ) where
1643 F: FnMut(i32, i32, u32, u32, Color),
1644 {
1645 self.shape_until_scroll(font_system, false);
1646 let mut renderer = crate::LegacyRenderer {
1647 font_system,
1648 cache,
1649 callback,
1650 };
1651 for run in self.layout_runs() {
1652 for glyph in run.glyphs {
1653 let physical_glyph = glyph.physical((0., run.line_y), 1.0);
1654 let glyph_color = glyph.color_opt.map_or(color, |some| some);
1655 renderer.glyph(physical_glyph, glyph_color);
1656 }
1657 render_decoration(&mut renderer, &run, color);
1658 }
1659 }
1660
1661 pub fn render<R: Renderer>(
1665 &mut self,
1666 font_system: &mut FontSystem,
1667 renderer: &mut R,
1668 color: Color,
1669 ) {
1670 self.shape_until_scroll(font_system, false);
1671 for run in self.layout_runs() {
1672 for glyph in run.glyphs {
1673 let physical_glyph = glyph.physical((0., run.line_y), 1.0);
1674 let glyph_color = glyph.color_opt.map_or(color, |some| some);
1675 renderer.glyph(physical_glyph, glyph_color);
1676 }
1677 render_decoration(renderer, &run, color);
1679 }
1680 }
1681}
1682
1683impl BorrowedWithFontSystem<'_, Buffer> {
1684 pub fn shape_until_cursor(&mut self, cursor: Cursor, prune: bool) {
1686 self.inner
1687 .shape_until_cursor(self.font_system, cursor, prune);
1688 }
1689
1690 pub fn line_shape(&mut self, line_i: usize) -> Option<&ShapeLine> {
1692 self.inner.line_shape(self.font_system, line_i)
1693 }
1694
1695 pub fn line_layout(&mut self, line_i: usize) -> Option<&[LayoutLine]> {
1697 self.inner.line_layout(self.font_system, line_i)
1698 }
1699
1700 pub fn set_metrics(&mut self, metrics: Metrics) {
1706 self.inner.set_metrics(metrics);
1707 }
1708
1709 pub fn set_hinting(&mut self, hinting: Hinting) {
1711 self.inner.set_hinting(hinting);
1712 }
1713
1714 pub fn set_wrap(&mut self, wrap: Wrap) {
1716 self.inner.set_wrap(wrap);
1717 }
1718
1719 pub fn set_direction(&mut self, direction: Direction) {
1721 self.inner.set_direction(direction);
1722 }
1723
1724 pub fn set_ellipsize(&mut self, ellipsize: Ellipsize) {
1726 self.inner.set_ellipsize(ellipsize);
1727 }
1728
1729 pub fn set_size(&mut self, width_opt: Option<f32>, height_opt: Option<f32>) {
1731 self.inner.set_size(width_opt, height_opt);
1732 }
1733
1734 pub fn set_metrics_and_size(
1740 &mut self,
1741 metrics: Metrics,
1742 width_opt: Option<f32>,
1743 height_opt: Option<f32>,
1744 ) {
1745 self.inner
1746 .set_metrics_and_size(metrics, width_opt, height_opt);
1747 }
1748
1749 pub fn set_tab_width(&mut self, tab_width: u16) {
1753 self.inner.set_tab_width(tab_width);
1754 }
1755
1756 pub fn set_monospace_width(&mut self, monospace_width: Option<f32>) {
1758 self.inner.set_monospace_width(monospace_width);
1759 }
1760
1761 pub fn set_text(
1763 &mut self,
1764 text: &str,
1765 attrs: &Attrs,
1766 shaping: Shaping,
1767 alignment: Option<Align>,
1768 ) {
1769 self.inner.set_text(text, attrs, shaping, alignment);
1770 }
1771
1772 pub fn set_rich_text<'r, 's, I>(
1774 &mut self,
1775 spans: I,
1776 default_attrs: &Attrs,
1777 shaping: Shaping,
1778 alignment: Option<Align>,
1779 ) where
1780 I: IntoIterator<Item = (&'s str, Attrs<'r>)>,
1781 {
1782 self.inner
1783 .set_rich_text(spans, default_attrs, shaping, alignment);
1784 }
1785
1786 pub fn shape_until_scroll(&mut self, prune: bool) {
1790 self.inner.shape_until_scroll(self.font_system, prune);
1791 }
1792
1793 pub fn layout_runs(&mut self) -> LayoutRunIter<'_> {
1797 self.inner.shape_until_scroll(self.font_system, false);
1798 self.inner.layout_runs()
1799 }
1800
1801 pub fn hit(&mut self, x: f32, y: f32) -> Option<Cursor> {
1805 self.inner.shape_until_scroll(self.font_system, false);
1806 self.inner.hit(x, y)
1807 }
1808
1809 pub fn cursor_motion(
1811 &mut self,
1812 cursor: Cursor,
1813 cursor_x_opt: Option<i32>,
1814 motion: Motion,
1815 ) -> Option<(Cursor, Option<i32>)> {
1816 self.inner
1817 .cursor_motion(self.font_system, cursor, cursor_x_opt, motion)
1818 }
1819
1820 #[cfg(feature = "swash")]
1824 pub fn draw<F>(&mut self, cache: &mut crate::SwashCache, color: Color, f: F)
1825 where
1826 F: FnMut(i32, i32, u32, u32, Color),
1827 {
1828 self.inner.draw(self.font_system, cache, color, f);
1829 }
1830}