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