1#[cfg(not(feature = "std"))]
4use alloc::{
5 string::{String, ToString},
6 vec::Vec,
7};
8use core::{cmp, fmt};
9use peniko::kurbo::{Point, Size};
10use unicode_segmentation::UnicodeSegmentation;
11
12use crate::{Attrs, AttrsList, LayoutGlyph, LayoutLine, ShapeLine, TextLayoutLine, Wrap};
13#[cfg(feature = "swash")]
14use peniko::Color;
15
16pub struct HitPoint {
17 pub line: usize,
19 pub index: usize,
21 pub is_inside: bool,
28}
29
30pub struct HitPosition {
31 pub line: usize,
33 pub point: Point,
35 pub glyph_ascent: f64,
37 pub glyph_descent: f64,
39}
40
41#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
43pub struct Cursor {
44 pub line: usize,
46 pub index: usize,
48 pub affinity: Affinity,
51}
52
53impl Cursor {
54 pub const fn new(line: usize, index: usize) -> Self {
56 Self::new_with_affinity(line, index, Affinity::Before)
57 }
58
59 pub const fn new_with_affinity(line: usize, index: usize, affinity: Affinity) -> Self {
61 Self {
62 line,
63 index,
64 affinity,
65 }
66 }
67}
68
69#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
71pub enum Affinity {
72 Before,
73 After,
74}
75
76impl Affinity {
77 pub fn before(&self) -> bool {
78 *self == Self::Before
79 }
80
81 pub fn after(&self) -> bool {
82 *self == Self::After
83 }
84
85 pub fn from_before(before: bool) -> Self {
86 if before {
87 Self::Before
88 } else {
89 Self::After
90 }
91 }
92
93 pub fn from_after(after: bool) -> Self {
94 if after {
95 Self::After
96 } else {
97 Self::Before
98 }
99 }
100}
101
102impl Default for Affinity {
103 fn default() -> Self {
104 Affinity::Before
105 }
106}
107
108pub struct LayoutCursor {
110 pub line: usize,
111 pub layout: usize,
112 pub glyph: usize,
113}
114
115impl LayoutCursor {
116 pub fn new(line: usize, layout: usize, glyph: usize) -> Self {
117 Self {
118 line,
119 layout,
120 glyph,
121 }
122 }
123}
124
125pub struct LayoutRun<'a> {
127 pub line_i: usize,
129 pub text: &'a str,
131 pub rtl: bool,
133 pub glyphs: &'a [LayoutGlyph],
135 pub line_y: f32,
137 pub line_w: f32,
139 pub line_height: f32,
141 pub glyph_ascent: f32,
143 pub glyph_descent: f32,
145}
146
147impl<'a> LayoutRun<'a> {
148 pub fn highlight(&self, cursor_start: Cursor, cursor_end: Cursor) -> Option<(f32, f32)> {
153 let mut x_start = None;
154 let mut x_end = None;
155 let rtl_factor = if self.rtl { 1. } else { 0. };
156 let ltr_factor = 1. - rtl_factor;
157 for glyph in self.glyphs.iter() {
158 let cursor = self.cursor_from_glyph_left(glyph);
159 if cursor >= cursor_start && cursor <= cursor_end {
160 if x_start.is_none() {
161 x_start = Some(glyph.x + glyph.w * rtl_factor);
162 }
163 x_end = Some(glyph.x + glyph.w * rtl_factor);
164 }
165 let cursor = self.cursor_from_glyph_right(glyph);
166 if cursor >= cursor_start && cursor <= cursor_end {
167 if x_start.is_none() {
168 x_start = Some(glyph.x + glyph.w * ltr_factor);
169 }
170 x_end = Some(glyph.x + glyph.w * ltr_factor);
171 }
172 }
173 if let Some(x_start) = x_start {
174 let x_end = x_end.expect("end of cursor not found");
175 let (x_start, x_end) = if x_start < x_end {
176 (x_start, x_end)
177 } else {
178 (x_end, x_start)
179 };
180 Some((x_start, x_end - x_start))
181 } else {
182 None
183 }
184 }
185
186 fn cursor_from_glyph_left(&self, glyph: &LayoutGlyph) -> Cursor {
187 if self.rtl {
188 Cursor::new_with_affinity(self.line_i, glyph.end, Affinity::Before)
189 } else {
190 Cursor::new_with_affinity(self.line_i, glyph.start, Affinity::After)
191 }
192 }
193
194 fn cursor_from_glyph_right(&self, glyph: &LayoutGlyph) -> Cursor {
195 if self.rtl {
196 Cursor::new_with_affinity(self.line_i, glyph.start, Affinity::After)
197 } else {
198 Cursor::new_with_affinity(self.line_i, glyph.end, Affinity::Before)
199 }
200 }
201}
202
203pub struct LayoutRunIter<'b> {
205 buffer: &'b TextLayout,
206 line_i: usize,
207 layout_i: usize,
208 remaining_len: usize,
209 line_y: f32,
210 total_layout: i32,
211}
212
213impl<'b> LayoutRunIter<'b> {
214 pub fn new(buffer: &'b TextLayout) -> Self {
215 let total_layout_lines: usize = buffer
216 .lines
217 .iter()
218 .map(|line| {
219 line.layout_opt()
220 .as_ref()
221 .map(|layout| layout.len())
222 .unwrap_or_default()
223 })
224 .sum();
225 let top_cropped_layout_lines =
226 total_layout_lines.saturating_sub(buffer.scroll.try_into().unwrap_or_default());
227 let maximum_lines = i32::MAX;
228 let bottom_cropped_layout_lines =
229 if top_cropped_layout_lines > maximum_lines.try_into().unwrap_or_default() {
230 maximum_lines.try_into().unwrap_or_default()
231 } else {
232 top_cropped_layout_lines
233 };
234
235 Self {
236 buffer,
237 line_i: 0,
238 layout_i: 0,
239 remaining_len: bottom_cropped_layout_lines,
240 line_y: 0.0,
241 total_layout: 0,
242 }
243 }
244}
245
246impl<'b> Iterator for LayoutRunIter<'b> {
247 type Item = LayoutRun<'b>;
248
249 fn size_hint(&self) -> (usize, Option<usize>) {
250 (self.remaining_len, Some(self.remaining_len))
251 }
252
253 fn next(&mut self) -> Option<Self::Item> {
254 while let Some(line) = self.buffer.lines.get(self.line_i) {
255 let shape = line.shape_opt().as_ref()?;
256 let layout = line.layout_opt().as_ref()?;
257 while let Some(layout_line) = layout.get(self.layout_i) {
258 self.layout_i += 1;
259
260 let scrolled = self.total_layout < self.buffer.scroll;
261 self.total_layout += 1;
262 if scrolled {
263 continue;
264 }
265
266 let line_height = layout_line.line_ascent + layout_line.line_descent;
267 self.line_y += line_height;
268 if self.line_y > self.buffer.height {
269 return None;
270 }
271
272 let offset =
273 (line_height - (layout_line.glyph_ascent + layout_line.glyph_descent)) / 2.0;
274
275 self.remaining_len -= 1;
276 return Some(LayoutRun {
277 line_i: self.line_i,
278 text: line.text(),
279 rtl: shape.rtl,
280 glyphs: &layout_line.glyphs,
281 line_y: self.line_y - offset - layout_line.glyph_descent,
282 line_w: layout_line.w,
283 glyph_ascent: layout_line.glyph_ascent,
284 glyph_descent: layout_line.glyph_descent,
285 line_height,
286 });
287 }
288 self.line_i += 1;
289 self.layout_i = 0;
290 }
291
292 None
293 }
294}
295
296impl<'b> ExactSizeIterator for LayoutRunIter<'b> {}
297
298#[derive(Clone, Copy, Debug, Default, PartialEq)]
300pub struct Metrics {
301 pub font_size: f32,
303 pub line_height: f32,
305}
306
307impl Metrics {
308 pub const fn new(font_size: f32, line_height: f32) -> Self {
309 Self {
310 font_size,
311 line_height,
312 }
313 }
314
315 pub fn scale(self, scale: f32) -> Self {
316 Self {
317 font_size: self.font_size * scale,
318 line_height: self.line_height * scale,
319 }
320 }
321}
322
323impl fmt::Display for Metrics {
324 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
325 write!(f, "{}px / {}px", self.font_size, self.line_height)
326 }
327}
328
329#[derive(Clone)]
331pub struct TextLayout {
332 pub lines: Vec<TextLayoutLine>,
334 width: f32,
335 height: f32,
336 scroll: i32,
337 redraw: bool,
339 wrap: Wrap,
340 tab_width: usize,
341}
342
343impl TextLayout {
344 pub fn new() -> Self {
350 let mut buffer = Self {
351 lines: Vec::new(),
352 width: f32::MAX,
353 height: f32::MAX,
354 scroll: 0,
355 redraw: false,
356 wrap: Wrap::Word,
357 tab_width: 8,
358 };
359 buffer.set_text("", AttrsList::new(Attrs::new()));
360 buffer
361 }
362
363 fn relayout(&mut self) {
364 #[cfg(all(feature = "std", not(target_arch = "wasm32")))]
365 let instant = std::time::Instant::now();
366
367 for line in &mut self.lines {
368 if line.shape_opt().is_some() {
369 line.reset_layout();
370 line.layout(self.width, self.wrap);
371 }
372 }
373
374 self.redraw = true;
375
376 #[cfg(all(feature = "std", not(target_arch = "wasm32")))]
377 log::debug!("relayout: {:?}", instant.elapsed());
378 }
379
380 pub fn shape_until(&mut self, lines: i32) -> i32 {
382 #[cfg(all(feature = "std", not(target_arch = "wasm32")))]
383 let instant = std::time::Instant::now();
384
385 let mut reshaped = 0;
386 let mut total_layout = 0;
387 for line in &mut self.lines {
388 if total_layout >= lines {
389 break;
390 }
391
392 if line.shape_opt().is_none() {
393 reshaped += 1;
394 }
395 let layout = line.layout(self.width, self.wrap);
396 total_layout += layout.len() as i32;
397 }
398
399 if reshaped > 0 {
400 #[cfg(all(feature = "std", not(target_arch = "wasm32")))]
401 log::debug!("shape_until {}: {:?}", reshaped, instant.elapsed());
402 self.redraw = true;
403 }
404
405 total_layout
406 }
407
408 pub fn shape_until_cursor(&mut self, cursor: Cursor) {
410 #[cfg(all(feature = "std", not(target_arch = "wasm32")))]
411 let instant = std::time::Instant::now();
412
413 let mut reshaped = 0;
414 let mut layout_i = 0;
415 for (line_i, line) in self.lines.iter_mut().enumerate() {
416 if line_i > cursor.line {
417 break;
418 }
419
420 if line.shape_opt().is_none() {
421 reshaped += 1;
422 }
423 let layout = line.layout(self.width, self.wrap);
424 if line_i == cursor.line {
425 let layout_cursor = self.layout_cursor(&cursor);
426 layout_i += layout_cursor.layout as i32;
427 break;
428 } else {
429 layout_i += layout.len() as i32;
430 }
431 }
432
433 if reshaped > 0 {
434 #[cfg(all(feature = "std", not(target_arch = "wasm32")))]
435 log::debug!("shape_until_cursor {}: {:?}", reshaped, instant.elapsed());
436 self.redraw = true;
437 }
438
439 let lines = i32::MAX;
440 if layout_i < self.scroll {
441 self.scroll = layout_i;
442 } else if layout_i >= self.scroll + lines {
443 self.scroll = layout_i - (lines - 1);
444 }
445
446 self.shape_until_scroll();
447 }
448
449 pub fn shape_until_scroll(&mut self) {
451 let lines = i32::MAX;
452
453 let scroll_end = self.scroll + lines;
454 let total_layout = self.shape_until(scroll_end);
455
456 self.scroll = cmp::max(0, cmp::min(total_layout - (lines - 1), self.scroll));
457 }
458
459 pub fn layout_cursor(&self, cursor: &Cursor) -> LayoutCursor {
460 let line = &self.lines[cursor.line];
461
462 let layout = line.layout_opt().as_ref().expect("layout not found");
464 for (layout_i, layout_line) in layout.iter().enumerate() {
465 for (glyph_i, glyph) in layout_line.glyphs.iter().enumerate() {
466 let cursor_end =
467 Cursor::new_with_affinity(cursor.line, glyph.end, Affinity::Before);
468 let cursor_start =
469 Cursor::new_with_affinity(cursor.line, glyph.start, Affinity::After);
470 let (cursor_left, cursor_right) = if glyph.level.is_ltr() {
471 (cursor_start, cursor_end)
472 } else {
473 (cursor_end, cursor_start)
474 };
475 if *cursor == cursor_left {
476 return LayoutCursor::new(cursor.line, layout_i, glyph_i);
477 }
478 if *cursor == cursor_right {
479 return LayoutCursor::new(cursor.line, layout_i, glyph_i + 1);
480 }
481 }
482 }
483
484 LayoutCursor::new(cursor.line, 0, 0)
487 }
488
489 pub fn line_shape(&mut self, line_i: usize) -> Option<&ShapeLine> {
491 let line = self.lines.get_mut(line_i)?;
492 Some(line.shape())
493 }
494
495 pub fn line_layout(&mut self, line_i: usize) -> Option<&[LayoutLine]> {
497 let line = self.lines.get_mut(line_i)?;
498 Some(line.layout(self.width, self.wrap))
499 }
500
501 pub fn wrap(&self) -> Wrap {
503 self.wrap
504 }
505
506 pub fn set_wrap(&mut self, wrap: Wrap) {
508 if wrap != self.wrap {
509 self.wrap = wrap;
510 self.relayout();
511 self.shape_until_scroll();
512 }
513 }
514
515 pub fn size(&self) -> Size {
516 self.layout_runs()
517 .fold(Size::new(0.0, 0.0), |mut size, run| {
518 let new_width = run.line_w as f64;
519 if new_width > size.width {
520 size.width = new_width;
521 }
522
523 size.height += run.line_height as f64;
524
525 size
526 })
527 }
528 pub fn set_size(&mut self, width: f32, height: f32) {
530 let clamped_width = width.max(0.0);
531 let clamped_height = height.max(0.0);
532
533 if clamped_width != self.width || clamped_height != self.height {
534 self.width = clamped_width;
535 self.height = clamped_height;
536 self.relayout();
537 self.shape_until_scroll();
538 }
539 }
540
541 pub fn scroll(&self) -> i32 {
543 self.scroll
544 }
545
546 pub fn set_scroll(&mut self, scroll: i32) {
548 if scroll != self.scroll {
549 self.scroll = scroll;
550 self.redraw = true;
551 }
552 }
553
554 pub fn set_text(&mut self, text: &str, attrs: AttrsList) {
556 self.lines.clear();
557 let mut attrs = attrs;
558 let mut start_index = 0;
559 for line in text.split_terminator('\n') {
560 let l = line.len();
561 let (line, had_r) = if l > 0 && line.as_bytes()[l - 1] == b'\r' {
562 (&line[0..l - 1], true)
563 } else {
564 (line, false)
565 };
566 let new_attrs = attrs.split_off(line.len() + 1 + if had_r { 1 } else { 0 });
567 self.lines.push(TextLayoutLine::new(
568 line.to_string(),
569 attrs.clone(),
570 start_index,
571 self.tab_width,
572 ));
573 attrs = new_attrs;
574
575 start_index += l + 1 + if had_r { 1 } else { 0 };
576 }
577 if self.lines.is_empty() {
579 self.lines
580 .push(TextLayoutLine::new(String::new(), attrs, 0, self.tab_width));
581 }
582
583 self.scroll = 0;
584
585 self.shape_until_scroll();
586 }
587
588 pub fn set_tab_width(&mut self, tab_width: usize) {
589 self.tab_width = tab_width;
590 }
591
592 pub fn redraw(&self) -> bool {
594 self.redraw
595 }
596
597 pub fn set_redraw(&mut self, redraw: bool) {
599 self.redraw = redraw;
600 }
601
602 pub fn layout_runs(&self) -> LayoutRunIter {
604 LayoutRunIter::new(self)
605 }
606
607 pub fn hit_point(&self, point: Point) -> HitPoint {
608 let x = point.x as f32;
609 let y = point.y as f32;
610 let mut hit_point = HitPoint {
611 index: 0,
612 line: 0,
613 is_inside: false,
614 };
615
616 let mut runs = self.layout_runs().peekable();
617 let mut first_run = true;
618 while let Some(run) = runs.next() {
619 let line_y = run.line_y;
620
621 if first_run && y < line_y - run.line_height {
622 first_run = false;
623 hit_point.line = run.line_i;
624 hit_point.index = 0;
625 } else if y >= line_y - run.line_height && y < line_y {
626 let mut new_cursor_glyph = run.glyphs.len();
627 let mut new_cursor_char = 0;
628
629 let mut first_glyph = true;
630
631 'hit: for (glyph_i, glyph) in run.glyphs.iter().enumerate() {
632 if first_glyph {
633 first_glyph = false;
634 if (run.rtl && x > glyph.x) || (!run.rtl && x < 0.0) {
635 new_cursor_glyph = 0;
636 new_cursor_char = 0;
637 }
638 }
639 if x >= glyph.x && x <= glyph.x + glyph.w {
640 new_cursor_glyph = glyph_i;
641
642 let cluster = &run.text[glyph.start..glyph.end];
643 let total = cluster.grapheme_indices(true).count();
644 let mut egc_x = glyph.x;
645 let egc_w = glyph.w / (total as f32);
646 for (egc_i, egc) in cluster.grapheme_indices(true) {
647 if x >= egc_x && x <= egc_x + egc_w {
648 new_cursor_char = egc_i;
649
650 let right_half = x >= egc_x + egc_w / 2.0;
651 if right_half != glyph.level.is_rtl() {
652 new_cursor_char += egc.len();
654 }
655 break 'hit;
656 }
657 egc_x += egc_w;
658 }
659
660 let right_half = x >= glyph.x + glyph.w / 2.0;
661 if right_half != glyph.level.is_rtl() {
662 new_cursor_char = cluster.len();
664 }
665 break 'hit;
666 }
667 }
668
669 hit_point.line = run.line_i;
670 hit_point.index = 0;
671
672 match run.glyphs.get(new_cursor_glyph) {
673 Some(glyph) => {
674 hit_point.index = glyph.start + new_cursor_char;
676 hit_point.is_inside = true;
677 }
678 None => {
679 if let Some(glyph) = run.glyphs.last() {
680 hit_point.index = glyph.end;
682 }
683 }
684 }
685
686 break;
687 } else if runs.peek().is_none() && y > run.line_y {
688 let mut new_cursor = Cursor::new(run.line_i, 0);
689 if let Some(glyph) = run.glyphs.last() {
690 new_cursor = run.cursor_from_glyph_right(glyph);
691 }
692 hit_point.line = new_cursor.line;
693 hit_point.index = new_cursor.index;
694 }
695 }
696
697 hit_point
698 }
699
700 pub fn line_col_position(&self, line: usize, col: usize) -> HitPosition {
701 let mut last_glyph: Option<&LayoutGlyph> = None;
702 let mut last_line = 0;
703 let mut last_line_y = 0.0;
704 let mut last_glyph_ascent = 0.0;
705 let mut last_glyph_descent = 0.0;
706 for (current_line, run) in self.layout_runs().enumerate() {
707 for glyph in run.glyphs {
708 if line == run.line_i {
709 if glyph.start > col {
710 return HitPosition {
711 line: last_line,
712 point: Point::new(
713 last_glyph.map(|g| (g.x + g.w) as f64).unwrap_or(0.0),
714 last_line_y as f64,
715 ),
716 glyph_ascent: last_glyph_ascent as f64,
717 glyph_descent: last_glyph_descent as f64,
718 };
719 }
720 if (glyph.start..glyph.end).contains(&col) {
721 return HitPosition {
722 line: current_line,
723 point: Point::new(glyph.x as f64, run.line_y as f64),
724 glyph_ascent: run.glyph_ascent as f64,
725 glyph_descent: run.glyph_descent as f64,
726 };
727 }
728 } else if run.line_i > line {
729 return HitPosition {
730 line: last_line,
731 point: Point::new(
732 last_glyph.map(|g| (g.x + g.w) as f64).unwrap_or(0.0),
733 last_line_y as f64,
734 ),
735 glyph_ascent: last_glyph_ascent as f64,
736 glyph_descent: last_glyph_descent as f64,
737 };
738 }
739 last_glyph = Some(glyph);
740 }
741 last_line = current_line;
742 last_line_y = run.line_y;
743 last_glyph_ascent = run.glyph_ascent;
744 last_glyph_descent = run.glyph_descent;
745 }
746
747 HitPosition {
748 line: last_line,
749 point: Point::new(
750 last_glyph.map(|g| (g.x + g.w) as f64).unwrap_or(0.0),
751 last_line_y as f64,
752 ),
753 glyph_ascent: last_glyph_ascent as f64,
754 glyph_descent: last_glyph_descent as f64,
755 }
756 }
757
758 pub fn hit_position(&self, idx: usize) -> HitPosition {
759 let mut last_line = 0;
760 let mut last_end: usize = 0;
761 let mut offset = 0;
762 let mut last_glyph_width = 0.0;
763 let mut last_position = HitPosition {
764 line: 0,
765 point: Point::ZERO,
766 glyph_ascent: 0.0,
767 glyph_descent: 0.0,
768 };
769 for (line, run) in self.layout_runs().enumerate() {
770 if run.line_i > last_line {
771 last_line = run.line_i;
772 offset += last_end + 1;
773 }
774 for glyph in run.glyphs {
775 if glyph.start + offset > idx {
776 last_position.point.x += last_glyph_width as f64;
777 return last_position;
778 }
779 last_end = glyph.end;
780 last_glyph_width = glyph.w;
781 last_position = HitPosition {
782 line,
783 point: Point::new(glyph.x as f64, run.line_y as f64),
784 glyph_ascent: run.glyph_ascent as f64,
785 glyph_descent: run.glyph_descent as f64,
786 };
787 if (glyph.start + offset..glyph.end + offset).contains(&idx) {
788 return last_position;
789 }
790 }
791 }
792
793 if idx > 0 {
794 last_position.point.x += last_glyph_width as f64;
795 return last_position;
796 }
797
798 HitPosition {
799 line: 0,
800 point: Point::ZERO,
801 glyph_ascent: 0.0,
802 glyph_descent: 0.0,
803 }
804 }
805
806 pub fn hit(&self, x: f32, y: f32) -> Option<Cursor> {
808 #[cfg(all(feature = "std", not(target_arch = "wasm32")))]
809 let instant = std::time::Instant::now();
810
811 let mut new_cursor_opt = None;
812
813 let mut runs = self.layout_runs().peekable();
814 let mut first_run = true;
815 while let Some(run) = runs.next() {
816 let line_y = run.line_y;
817
818 if first_run && y < line_y - run.line_height {
819 first_run = false;
820 let new_cursor = Cursor::new(run.line_i, 0);
821 new_cursor_opt = Some(new_cursor);
822 } else if y >= line_y - run.line_height && y < line_y {
823 let mut new_cursor_glyph = run.glyphs.len();
824 let mut new_cursor_char = 0;
825 let mut new_cursor_affinity = Affinity::After;
826
827 let mut first_glyph = true;
828
829 'hit: for (glyph_i, glyph) in run.glyphs.iter().enumerate() {
830 if first_glyph {
831 first_glyph = false;
832 if (run.rtl && x > glyph.x) || (!run.rtl && x < 0.0) {
833 new_cursor_glyph = 0;
834 new_cursor_char = 0;
835 }
836 }
837 if x >= glyph.x && x <= glyph.x + glyph.w {
838 new_cursor_glyph = glyph_i;
839
840 let cluster = &run.text[glyph.start..glyph.end];
841 let total = cluster.grapheme_indices(true).count();
842 let mut egc_x = glyph.x;
843 let egc_w = glyph.w / (total as f32);
844 for (egc_i, egc) in cluster.grapheme_indices(true) {
845 if x >= egc_x && x <= egc_x + egc_w {
846 new_cursor_char = egc_i;
847
848 let right_half = x >= egc_x + egc_w / 2.0;
849 if right_half != glyph.level.is_rtl() {
850 new_cursor_char += egc.len();
852 new_cursor_affinity = Affinity::Before;
853 }
854 break 'hit;
855 }
856 egc_x += egc_w;
857 }
858
859 let right_half = x >= glyph.x + glyph.w / 2.0;
860 if right_half != glyph.level.is_rtl() {
861 new_cursor_char = cluster.len();
863 new_cursor_affinity = Affinity::Before;
864 }
865 break 'hit;
866 }
867 }
868
869 let mut new_cursor = Cursor::new(run.line_i, 0);
870
871 match run.glyphs.get(new_cursor_glyph) {
872 Some(glyph) => {
873 new_cursor.index = glyph.start + new_cursor_char;
875 new_cursor.affinity = new_cursor_affinity;
876 }
877 None => {
878 if let Some(glyph) = run.glyphs.last() {
879 new_cursor.index = glyph.end;
881 new_cursor.affinity = Affinity::Before;
882 }
883 }
884 }
885
886 new_cursor_opt = Some(new_cursor);
887
888 break;
889 } else if runs.peek().is_none() && y > run.line_y {
890 let mut new_cursor = Cursor::new(run.line_i, 0);
891 if let Some(glyph) = run.glyphs.last() {
892 new_cursor = run.cursor_from_glyph_right(glyph);
893 }
894 new_cursor_opt = Some(new_cursor);
895 }
896 }
897
898 #[cfg(all(feature = "std", not(target_arch = "wasm32")))]
899 log::trace!("click({}, {}): {:?}", x, y, instant.elapsed());
900
901 new_cursor_opt
902 }
903
904 #[cfg(feature = "swash")]
906 pub fn draw<F>(&self, cache: &mut crate::SwashCache, color: Color, mut f: F)
907 where
908 F: FnMut(i32, i32, u32, u32, Color),
909 {
910 for run in self.layout_runs() {
911 for glyph in run.glyphs.iter() {
912 let (cache_key, x_int, y_int) = (glyph.cache_key, glyph.x_int, glyph.y_int);
913
914 let glyph_color = glyph.color;
915
916 cache.with_pixels(cache_key, glyph_color, |x, y, color| {
917 f(x_int + x, run.line_y as i32 + y_int + y, 1, 1, color);
918 });
919 }
920 }
921 }
922}