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