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