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 scroll: Scroll,
212 redraw: bool,
214 wrap: Wrap,
215 monospace_width: Option<f32>,
216 tab_width: u16,
217}
218
219impl Clone for Buffer {
220 fn clone(&self) -> Self {
221 Self {
222 lines: self.lines.clone(),
223 metrics: self.metrics,
224 width_opt: self.width_opt,
225 height_opt: self.height_opt,
226 scroll: self.scroll,
227 redraw: self.redraw,
228 wrap: self.wrap,
229 monospace_width: self.monospace_width,
230 tab_width: self.tab_width,
231 }
232 }
233}
234
235impl Buffer {
236 pub fn new_empty(metrics: Metrics) -> Self {
248 assert_ne!(metrics.line_height, 0.0, "line height cannot be 0");
249 Self {
250 lines: Vec::new(),
251 metrics,
252 width_opt: None,
253 height_opt: None,
254 scroll: Scroll::default(),
255 redraw: false,
256 wrap: Wrap::WordOrGlyph,
257 monospace_width: None,
258 tab_width: 8,
259 }
260 }
261
262 pub fn new(font_system: &mut FontSystem, metrics: Metrics) -> Self {
268 let mut buffer = Self::new_empty(metrics);
269 buffer.set_text(font_system, "", Attrs::new(), Shaping::Advanced);
270 buffer
271 }
272
273 pub fn borrow_with<'a>(
275 &'a mut self,
276 font_system: &'a mut FontSystem,
277 ) -> BorrowedWithFontSystem<'a, Buffer> {
278 BorrowedWithFontSystem {
279 inner: self,
280 font_system,
281 }
282 }
283
284 fn relayout(&mut self, font_system: &mut FontSystem) {
285 #[cfg(all(feature = "std", not(target_arch = "wasm32")))]
286 let instant = std::time::Instant::now();
287
288 for line in &mut self.lines {
289 if line.shape_opt().is_some() {
290 line.reset_layout();
291 line.layout(
292 font_system,
293 self.metrics.font_size,
294 self.width_opt,
295 self.wrap,
296 self.monospace_width,
297 self.tab_width,
298 );
299 }
300 }
301
302 self.redraw = true;
303
304 #[cfg(all(feature = "std", not(target_arch = "wasm32")))]
305 log::debug!("relayout: {:?}", instant.elapsed());
306 }
307
308 #[allow(clippy::missing_panics_doc)]
310 pub fn shape_until_cursor(
311 &mut self,
312 font_system: &mut FontSystem,
313 cursor: Cursor,
314 prune: bool,
315 ) {
316 let metrics = self.metrics;
317 let old_scroll = self.scroll;
318
319 let layout_cursor = self
320 .layout_cursor(font_system, cursor)
321 .expect("shape_until_cursor invalid cursor");
322
323 let mut layout_y = 0.0;
324 let mut total_height = {
325 let layout = self
326 .line_layout(font_system, layout_cursor.line)
327 .expect("shape_until_cursor failed to scroll forwards");
328 (0..layout_cursor.layout).for_each(|layout_i| {
329 layout_y += layout[layout_i]
330 .line_height_opt
331 .unwrap_or(metrics.line_height);
332 });
333 layout_y
334 + layout[layout_cursor.layout]
335 .line_height_opt
336 .unwrap_or(metrics.line_height)
337 };
338
339 if self.scroll.line > layout_cursor.line
340 || (self.scroll.line == layout_cursor.line && self.scroll.vertical > layout_y)
341 {
342 self.scroll.line = layout_cursor.line;
344 self.scroll.vertical = layout_y;
345 } else if let Some(height) = self.height_opt {
346 let mut line_i = layout_cursor.line;
348 if line_i <= self.scroll.line {
349 if total_height > height + self.scroll.vertical {
351 self.scroll.vertical = total_height - height;
352 }
353 } else {
354 while line_i > self.scroll.line {
355 line_i -= 1;
356 let layout = self
357 .line_layout(font_system, line_i)
358 .expect("shape_until_cursor failed to scroll forwards");
359 for layout_line in layout.iter() {
360 total_height += layout_line.line_height_opt.unwrap_or(metrics.line_height);
361 }
362 if total_height > height + self.scroll.vertical {
363 self.scroll.line = line_i;
364 self.scroll.vertical = total_height - height;
365 }
366 }
367 }
368 }
369
370 if old_scroll != self.scroll {
371 self.redraw = true;
372 }
373
374 self.shape_until_scroll(font_system, prune);
375
376 if let Some(layout_cursor) = self.layout_cursor(font_system, cursor) {
378 if let Some(layout_lines) = self.line_layout(font_system, layout_cursor.line) {
379 if let Some(layout_line) = layout_lines.get(layout_cursor.layout) {
380 let (x_min, x_max) = if let Some(glyph) = layout_line
381 .glyphs
382 .get(layout_cursor.glyph)
383 .or_else(|| layout_line.glyphs.last())
384 {
385 let x_a = glyph.x;
387 let x_b = glyph.x + glyph.w;
388 (x_a.min(x_b), x_a.max(x_b))
389 } else {
390 (0.0, 0.0)
391 };
392 if x_min < self.scroll.horizontal {
393 self.scroll.horizontal = x_min;
394 self.redraw = true;
395 }
396 if let Some(width) = self.width_opt {
397 if x_max > self.scroll.horizontal + width {
398 self.scroll.horizontal = x_max - width;
399 self.redraw = true;
400 }
401 }
402 }
403 }
404 }
405 }
406
407 #[allow(clippy::missing_panics_doc)]
409 pub fn shape_until_scroll(&mut self, font_system: &mut FontSystem, prune: bool) {
410 let metrics = self.metrics;
411 let old_scroll = self.scroll;
412
413 loop {
414 while self.scroll.vertical < 0.0 {
416 if self.scroll.line > 0 {
417 let line_i = self.scroll.line - 1;
418 if let Some(layout) = self.line_layout(font_system, line_i) {
419 let mut layout_height = 0.0;
420 for layout_line in layout.iter() {
421 layout_height +=
422 layout_line.line_height_opt.unwrap_or(metrics.line_height);
423 }
424 self.scroll.line = line_i;
425 self.scroll.vertical += layout_height;
426 } else {
427 self.scroll.line = line_i;
429 self.scroll.vertical += metrics.line_height;
430 }
431 } else {
432 self.scroll.vertical = 0.0;
433 break;
434 }
435 }
436
437 let scroll_start = self.scroll.vertical;
438 let scroll_end = scroll_start + self.height_opt.unwrap_or(f32::INFINITY);
439
440 let mut total_height = 0.0;
441 for line_i in 0..self.lines.len() {
442 if line_i < self.scroll.line {
443 if prune {
444 self.lines[line_i].reset_shaping();
445 }
446 continue;
447 }
448 if total_height > scroll_end {
449 if prune {
450 self.lines[line_i].reset_shaping();
451 continue;
452 } else {
453 break;
454 }
455 }
456
457 let mut layout_height = 0.0;
458 let layout = self
459 .line_layout(font_system, line_i)
460 .expect("shape_until_scroll invalid line");
461 for layout_line in layout.iter() {
462 let line_height = layout_line.line_height_opt.unwrap_or(metrics.line_height);
463 layout_height += line_height;
464 total_height += line_height;
465 }
466
467 if line_i == self.scroll.line && layout_height < self.scroll.vertical {
470 self.scroll.line += 1;
471 self.scroll.vertical -= layout_height;
472 }
473 }
474
475 if total_height < scroll_end && self.scroll.line > 0 {
476 self.scroll.vertical -= scroll_end - total_height;
478 } else {
479 break;
481 }
482 }
483
484 if old_scroll != self.scroll {
485 self.redraw = true;
486 }
487 }
488
489 pub fn layout_cursor(
491 &mut self,
492 font_system: &mut FontSystem,
493 cursor: Cursor,
494 ) -> Option<LayoutCursor> {
495 let layout = self.line_layout(font_system, cursor.line)?;
496 for (layout_i, layout_line) in layout.iter().enumerate() {
497 for (glyph_i, glyph) in layout_line.glyphs.iter().enumerate() {
498 let cursor_end =
499 Cursor::new_with_affinity(cursor.line, glyph.end, Affinity::Before);
500 let cursor_start =
501 Cursor::new_with_affinity(cursor.line, glyph.start, Affinity::After);
502 let (cursor_left, cursor_right) = if glyph.level.is_ltr() {
503 (cursor_start, cursor_end)
504 } else {
505 (cursor_end, cursor_start)
506 };
507 if cursor == cursor_left {
508 return Some(LayoutCursor::new(cursor.line, layout_i, glyph_i));
509 }
510 if cursor == cursor_right {
511 return Some(LayoutCursor::new(cursor.line, layout_i, glyph_i + 1));
512 }
513 }
514 }
515
516 Some(LayoutCursor::new(cursor.line, 0, 0))
519 }
520
521 pub fn line_shape(
523 &mut self,
524 font_system: &mut FontSystem,
525 line_i: usize,
526 ) -> Option<&ShapeLine> {
527 let line = self.lines.get_mut(line_i)?;
528 Some(line.shape(font_system, self.tab_width))
529 }
530
531 pub fn line_layout(
533 &mut self,
534 font_system: &mut FontSystem,
535 line_i: usize,
536 ) -> Option<&[LayoutLine]> {
537 let line = self.lines.get_mut(line_i)?;
538 Some(line.layout(
539 font_system,
540 self.metrics.font_size,
541 self.width_opt,
542 self.wrap,
543 self.monospace_width,
544 self.tab_width,
545 ))
546 }
547
548 pub fn metrics(&self) -> Metrics {
550 self.metrics
551 }
552
553 pub fn set_metrics(&mut self, font_system: &mut FontSystem, metrics: Metrics) {
559 self.set_metrics_and_size(font_system, metrics, self.width_opt, self.height_opt);
560 }
561
562 pub fn wrap(&self) -> Wrap {
564 self.wrap
565 }
566
567 pub fn set_wrap(&mut self, font_system: &mut FontSystem, wrap: Wrap) {
569 if wrap != self.wrap {
570 self.wrap = wrap;
571 self.relayout(font_system);
572 self.shape_until_scroll(font_system, false);
573 }
574 }
575
576 pub fn monospace_width(&self) -> Option<f32> {
578 self.monospace_width
579 }
580
581 pub fn set_monospace_width(
583 &mut self,
584 font_system: &mut FontSystem,
585 monospace_width: Option<f32>,
586 ) {
587 if monospace_width != self.monospace_width {
588 self.monospace_width = monospace_width;
589 self.relayout(font_system);
590 self.shape_until_scroll(font_system, false);
591 }
592 }
593
594 pub fn tab_width(&self) -> u16 {
596 self.tab_width
597 }
598
599 pub fn set_tab_width(&mut self, font_system: &mut FontSystem, tab_width: u16) {
601 if tab_width == 0 {
603 return;
604 }
605 if tab_width != self.tab_width {
606 self.tab_width = tab_width;
607 for line in self.lines.iter_mut() {
609 if line.shape_opt().is_some() && line.text().contains('\t') {
610 line.reset_shaping();
611 }
612 }
613 self.redraw = true;
614 self.shape_until_scroll(font_system, false);
615 }
616 }
617
618 pub fn size(&self) -> (Option<f32>, Option<f32>) {
620 (self.width_opt, self.height_opt)
621 }
622
623 pub fn set_size(
625 &mut self,
626 font_system: &mut FontSystem,
627 width_opt: Option<f32>,
628 height_opt: Option<f32>,
629 ) {
630 self.set_metrics_and_size(font_system, self.metrics, width_opt, height_opt);
631 }
632
633 pub fn set_metrics_and_size(
639 &mut self,
640 font_system: &mut FontSystem,
641 metrics: Metrics,
642 width_opt: Option<f32>,
643 height_opt: Option<f32>,
644 ) {
645 let clamped_width_opt = width_opt.map(|width| width.max(0.0));
646 let clamped_height_opt = height_opt.map(|height| height.max(0.0));
647
648 if metrics != self.metrics
649 || clamped_width_opt != self.width_opt
650 || clamped_height_opt != self.height_opt
651 {
652 assert_ne!(metrics.font_size, 0.0, "font size cannot be 0");
653 self.metrics = metrics;
654 self.width_opt = clamped_width_opt;
655 self.height_opt = clamped_height_opt;
656 self.relayout(font_system);
657 self.shape_until_scroll(font_system, false);
658 }
659 }
660
661 pub fn scroll(&self) -> Scroll {
663 self.scroll
664 }
665
666 pub fn set_scroll(&mut self, scroll: Scroll) {
668 if scroll != self.scroll {
669 self.scroll = scroll;
670 self.redraw = true;
671 }
672 }
673
674 pub fn set_text(
676 &mut self,
677 font_system: &mut FontSystem,
678 text: &str,
679 attrs: Attrs,
680 shaping: Shaping,
681 ) {
682 self.lines.clear();
683 for (range, ending) in LineIter::new(text) {
684 self.lines.push(BufferLine::new(
685 &text[range],
686 ending,
687 AttrsList::new(attrs),
688 shaping,
689 ));
690 }
691 if self.lines.is_empty() {
692 self.lines.push(BufferLine::new(
693 "",
694 LineEnding::default(),
695 AttrsList::new(attrs),
696 shaping,
697 ));
698 }
699 self.scroll = Scroll::default();
700 self.shape_until_scroll(font_system, false);
701 }
702
703 pub fn set_rich_text<'r, 's, I>(
722 &mut self,
723 font_system: &mut FontSystem,
724 spans: I,
725 default_attrs: Attrs,
726 shaping: Shaping,
727 alignment: Option<Align>,
728 ) where
729 I: IntoIterator<Item = (&'s str, Attrs<'r>)>,
730 {
731 let mut end = 0;
732 let (string, spans_data): (String, Vec<_>) = spans
734 .into_iter()
735 .map(|(s, attrs)| {
736 let start = end;
737 end += s.len();
738 (s, (attrs, start..end))
739 })
740 .unzip();
741
742 let mut spans_iter = spans_data.into_iter();
743 let mut maybe_span = spans_iter.next();
744
745 let string_start = string.as_ptr() as usize;
747 let mut lines_iter = BidiParagraphs::new(&string).map(|line: &str| {
748 let start = line.as_ptr() as usize - string_start;
749 let end = start + line.len();
750 start..end
751 });
752 let mut maybe_line = lines_iter.next();
753 let line_ending = LineEnding::default();
755
756 let mut line_count = 0;
757 let mut attrs_list = self
758 .lines
759 .get_mut(line_count)
760 .map(BufferLine::reclaim_attrs)
761 .unwrap_or_else(|| AttrsList::new(Attrs::new()))
762 .reset(default_attrs);
763 let mut line_string = self
764 .lines
765 .get_mut(line_count)
766 .map(BufferLine::reclaim_text)
767 .unwrap_or_default();
768
769 loop {
770 let (Some(line_range), Some((attrs, span_range))) = (&maybe_line, &maybe_span) else {
771 if self.lines.len() == line_count {
773 self.lines.push(BufferLine::empty());
774 }
775 self.lines[line_count].reset_new(
776 String::new(),
777 line_ending,
778 AttrsList::new(default_attrs),
779 shaping,
780 );
781 line_count += 1;
782 break;
783 };
784
785 let start = line_range.start.max(span_range.start);
787 let end = line_range.end.min(span_range.end);
788 if start < end {
789 let text = &string[start..end];
790 let text_start = line_string.len();
791 line_string.push_str(text);
792 let text_end = line_string.len();
793 if *attrs != attrs_list.defaults() {
795 attrs_list.add_span(text_start..text_end, *attrs);
796 }
797 }
798
799 if span_range.end < line_range.end {
805 maybe_span = spans_iter.next();
806 } else {
807 maybe_line = lines_iter.next();
808 if maybe_line.is_some() {
809 let next_attrs_list = self
811 .lines
812 .get_mut(line_count + 1)
813 .map(BufferLine::reclaim_attrs)
814 .unwrap_or_else(|| AttrsList::new(Attrs::new()))
815 .reset(default_attrs);
816 let next_line_string = self
817 .lines
818 .get_mut(line_count + 1)
819 .map(BufferLine::reclaim_text)
820 .unwrap_or_default();
821 let prev_attrs_list = core::mem::replace(&mut attrs_list, next_attrs_list);
822 let prev_line_string = core::mem::replace(&mut line_string, next_line_string);
823 if self.lines.len() == line_count {
824 self.lines.push(BufferLine::empty());
825 }
826 self.lines[line_count].reset_new(
827 prev_line_string,
828 line_ending,
829 prev_attrs_list,
830 shaping,
831 );
832 line_count += 1;
833 } else {
834 if self.lines.len() == line_count {
836 self.lines.push(BufferLine::empty());
837 }
838 self.lines[line_count].reset_new(line_string, line_ending, attrs_list, shaping);
839 line_count += 1;
840 break;
841 }
842 }
843 }
844
845 self.lines.truncate(line_count);
847
848 self.lines.iter_mut().for_each(|line| {
849 line.set_align(alignment);
850 });
851
852 self.scroll = Scroll::default();
853
854 self.shape_until_scroll(font_system, false);
855 }
856
857 pub fn redraw(&self) -> bool {
859 self.redraw
860 }
861
862 pub fn set_redraw(&mut self, redraw: bool) {
864 self.redraw = redraw;
865 }
866
867 pub fn layout_runs(&self) -> LayoutRunIter {
869 LayoutRunIter::new(self)
870 }
871
872 pub fn hit(&self, x: f32, y: f32) -> Option<Cursor> {
874 #[cfg(all(feature = "std", not(target_arch = "wasm32")))]
875 let instant = std::time::Instant::now();
876
877 let mut new_cursor_opt = None;
878
879 let mut runs = self.layout_runs().peekable();
880 let mut first_run = true;
881 while let Some(run) = runs.next() {
882 let line_top = run.line_top;
883 let line_height = run.line_height;
884
885 if first_run && y < line_top {
886 first_run = false;
887 let new_cursor = Cursor::new(run.line_i, 0);
888 new_cursor_opt = Some(new_cursor);
889 } else if y >= line_top && y < line_top + line_height {
890 let mut new_cursor_glyph = run.glyphs.len();
891 let mut new_cursor_char = 0;
892 let mut new_cursor_affinity = Affinity::After;
893
894 let mut first_glyph = true;
895
896 'hit: for (glyph_i, glyph) in run.glyphs.iter().enumerate() {
897 if first_glyph {
898 first_glyph = false;
899 if (run.rtl && x > glyph.x) || (!run.rtl && x < 0.0) {
900 new_cursor_glyph = 0;
901 new_cursor_char = 0;
902 }
903 }
904 if x >= glyph.x && x <= glyph.x + glyph.w {
905 new_cursor_glyph = glyph_i;
906
907 let cluster = &run.text[glyph.start..glyph.end];
908 let total = cluster.grapheme_indices(true).count();
909 let mut egc_x = glyph.x;
910 let egc_w = glyph.w / (total as f32);
911 for (egc_i, egc) in cluster.grapheme_indices(true) {
912 if x >= egc_x && x <= egc_x + egc_w {
913 new_cursor_char = egc_i;
914
915 let right_half = x >= egc_x + egc_w / 2.0;
916 if right_half != glyph.level.is_rtl() {
917 new_cursor_char += egc.len();
919 new_cursor_affinity = Affinity::Before;
920 }
921 break 'hit;
922 }
923 egc_x += egc_w;
924 }
925
926 let right_half = x >= glyph.x + glyph.w / 2.0;
927 if right_half != glyph.level.is_rtl() {
928 new_cursor_char = cluster.len();
930 new_cursor_affinity = Affinity::Before;
931 }
932 break 'hit;
933 }
934 }
935
936 let mut new_cursor = Cursor::new(run.line_i, 0);
937
938 match run.glyphs.get(new_cursor_glyph) {
939 Some(glyph) => {
940 new_cursor.index = glyph.start + new_cursor_char;
942 new_cursor.affinity = new_cursor_affinity;
943 }
944 None => {
945 if let Some(glyph) = run.glyphs.last() {
946 new_cursor.index = glyph.end;
948 new_cursor.affinity = Affinity::Before;
949 }
950 }
951 }
952
953 new_cursor_opt = Some(new_cursor);
954
955 break;
956 } else if runs.peek().is_none() && y > run.line_y {
957 let mut new_cursor = Cursor::new(run.line_i, 0);
958 if let Some(glyph) = run.glyphs.last() {
959 new_cursor = run.cursor_from_glyph_right(glyph);
960 }
961 new_cursor_opt = Some(new_cursor);
962 }
963 }
964
965 #[cfg(all(feature = "std", not(target_arch = "wasm32")))]
966 log::trace!("click({}, {}): {:?}", x, y, instant.elapsed());
967
968 new_cursor_opt
969 }
970
971 pub fn cursor_motion(
973 &mut self,
974 font_system: &mut FontSystem,
975 mut cursor: Cursor,
976 mut cursor_x_opt: Option<i32>,
977 motion: Motion,
978 ) -> Option<(Cursor, Option<i32>)> {
979 match motion {
980 Motion::LayoutCursor(layout_cursor) => {
981 let layout = self.line_layout(font_system, layout_cursor.line)?;
982
983 let layout_line = match layout.get(layout_cursor.layout) {
984 Some(some) => some,
985 None => match layout.last() {
986 Some(some) => some,
987 None => {
988 return None;
989 }
990 },
991 };
992
993 let (new_index, new_affinity) = match layout_line.glyphs.get(layout_cursor.glyph) {
994 Some(glyph) => (glyph.start, Affinity::After),
995 None => match layout_line.glyphs.last() {
996 Some(glyph) => (glyph.end, Affinity::Before),
997 None => (0, Affinity::After),
999 },
1000 };
1001
1002 if cursor.line != layout_cursor.line
1003 || cursor.index != new_index
1004 || cursor.affinity != new_affinity
1005 {
1006 cursor.line = layout_cursor.line;
1007 cursor.index = new_index;
1008 cursor.affinity = new_affinity;
1009 }
1010 }
1011 Motion::Previous => {
1012 let line = self.lines.get(cursor.line)?;
1013 if cursor.index > 0 {
1014 let mut prev_index = 0;
1016 for (i, _) in line.text().grapheme_indices(true) {
1017 if i < cursor.index {
1018 prev_index = i;
1019 } else {
1020 break;
1021 }
1022 }
1023
1024 cursor.index = prev_index;
1025 cursor.affinity = Affinity::After;
1026 } else if cursor.line > 0 {
1027 cursor.line -= 1;
1028 cursor.index = self.lines.get(cursor.line)?.text().len();
1029 cursor.affinity = Affinity::After;
1030 }
1031 cursor_x_opt = None;
1032 }
1033 Motion::Next => {
1034 let line = self.lines.get(cursor.line)?;
1035 if cursor.index < line.text().len() {
1036 for (i, c) in line.text().grapheme_indices(true) {
1037 if i == cursor.index {
1038 cursor.index += c.len();
1039 cursor.affinity = Affinity::Before;
1040 break;
1041 }
1042 }
1043 } else if cursor.line + 1 < self.lines.len() {
1044 cursor.line += 1;
1045 cursor.index = 0;
1046 cursor.affinity = Affinity::Before;
1047 }
1048 cursor_x_opt = None;
1049 }
1050 Motion::Left => {
1051 let rtl_opt = self
1052 .line_shape(font_system, cursor.line)
1053 .map(|shape| shape.rtl);
1054 if let Some(rtl) = rtl_opt {
1055 if rtl {
1056 (cursor, cursor_x_opt) =
1057 self.cursor_motion(font_system, cursor, cursor_x_opt, Motion::Next)?;
1058 } else {
1059 (cursor, cursor_x_opt) = self.cursor_motion(
1060 font_system,
1061 cursor,
1062 cursor_x_opt,
1063 Motion::Previous,
1064 )?;
1065 }
1066 }
1067 }
1068 Motion::Right => {
1069 let rtl_opt = self
1070 .line_shape(font_system, cursor.line)
1071 .map(|shape| shape.rtl);
1072 if let Some(rtl) = rtl_opt {
1073 if rtl {
1074 (cursor, cursor_x_opt) = self.cursor_motion(
1075 font_system,
1076 cursor,
1077 cursor_x_opt,
1078 Motion::Previous,
1079 )?;
1080 } else {
1081 (cursor, cursor_x_opt) =
1082 self.cursor_motion(font_system, cursor, cursor_x_opt, Motion::Next)?;
1083 }
1084 }
1085 }
1086 Motion::Up => {
1087 let mut layout_cursor = self.layout_cursor(font_system, cursor)?;
1088
1089 if cursor_x_opt.is_none() {
1090 cursor_x_opt = Some(
1091 layout_cursor.glyph as i32, );
1093 }
1094
1095 if layout_cursor.layout > 0 {
1096 layout_cursor.layout -= 1;
1097 } else if layout_cursor.line > 0 {
1098 layout_cursor.line -= 1;
1099 layout_cursor.layout = usize::MAX;
1100 }
1101
1102 if let Some(cursor_x) = cursor_x_opt {
1103 layout_cursor.glyph = cursor_x as usize; }
1105
1106 (cursor, cursor_x_opt) = self.cursor_motion(
1107 font_system,
1108 cursor,
1109 cursor_x_opt,
1110 Motion::LayoutCursor(layout_cursor),
1111 )?;
1112 }
1113 Motion::Down => {
1114 let mut layout_cursor = self.layout_cursor(font_system, cursor)?;
1115
1116 let layout_len = self.line_layout(font_system, layout_cursor.line)?.len();
1117
1118 if cursor_x_opt.is_none() {
1119 cursor_x_opt = Some(
1120 layout_cursor.glyph as i32, );
1122 }
1123
1124 if layout_cursor.layout + 1 < layout_len {
1125 layout_cursor.layout += 1;
1126 } else if layout_cursor.line + 1 < self.lines.len() {
1127 layout_cursor.line += 1;
1128 layout_cursor.layout = 0;
1129 }
1130
1131 if let Some(cursor_x) = cursor_x_opt {
1132 layout_cursor.glyph = cursor_x as usize; }
1134
1135 (cursor, cursor_x_opt) = self.cursor_motion(
1136 font_system,
1137 cursor,
1138 cursor_x_opt,
1139 Motion::LayoutCursor(layout_cursor),
1140 )?;
1141 }
1142 Motion::Home => {
1143 let mut layout_cursor = self.layout_cursor(font_system, cursor)?;
1144 layout_cursor.glyph = 0;
1145 #[allow(unused_assignments)]
1146 {
1147 (cursor, cursor_x_opt) = self.cursor_motion(
1148 font_system,
1149 cursor,
1150 cursor_x_opt,
1151 Motion::LayoutCursor(layout_cursor),
1152 )?;
1153 }
1154 cursor_x_opt = None;
1155 }
1156 Motion::SoftHome => {
1157 let line = self.lines.get(cursor.line)?;
1158 cursor.index = line
1159 .text()
1160 .char_indices()
1161 .filter_map(|(i, c)| if c.is_whitespace() { None } else { Some(i) })
1162 .next()
1163 .unwrap_or(0);
1164 cursor_x_opt = None;
1165 }
1166 Motion::End => {
1167 let mut layout_cursor = self.layout_cursor(font_system, cursor)?;
1168 layout_cursor.glyph = usize::MAX;
1169 #[allow(unused_assignments)]
1170 {
1171 (cursor, cursor_x_opt) = self.cursor_motion(
1172 font_system,
1173 cursor,
1174 cursor_x_opt,
1175 Motion::LayoutCursor(layout_cursor),
1176 )?;
1177 }
1178 cursor_x_opt = None;
1179 }
1180 Motion::ParagraphStart => {
1181 cursor.index = 0;
1182 cursor_x_opt = None;
1183 }
1184 Motion::ParagraphEnd => {
1185 cursor.index = self.lines.get(cursor.line)?.text().len();
1186 cursor_x_opt = None;
1187 }
1188 Motion::PageUp => {
1189 if let Some(height) = self.height_opt {
1190 (cursor, cursor_x_opt) = self.cursor_motion(
1191 font_system,
1192 cursor,
1193 cursor_x_opt,
1194 Motion::Vertical(-height as i32),
1195 )?;
1196 }
1197 }
1198 Motion::PageDown => {
1199 if let Some(height) = self.height_opt {
1200 (cursor, cursor_x_opt) = self.cursor_motion(
1201 font_system,
1202 cursor,
1203 cursor_x_opt,
1204 Motion::Vertical(height as i32),
1205 )?;
1206 }
1207 }
1208 Motion::Vertical(px) => {
1209 let lines = px / self.metrics().line_height as i32;
1211 match lines.cmp(&0) {
1212 cmp::Ordering::Less => {
1213 for _ in 0..-lines {
1214 (cursor, cursor_x_opt) =
1215 self.cursor_motion(font_system, cursor, cursor_x_opt, Motion::Up)?;
1216 }
1217 }
1218 cmp::Ordering::Greater => {
1219 for _ in 0..lines {
1220 (cursor, cursor_x_opt) = self.cursor_motion(
1221 font_system,
1222 cursor,
1223 cursor_x_opt,
1224 Motion::Down,
1225 )?;
1226 }
1227 }
1228 cmp::Ordering::Equal => {}
1229 }
1230 }
1231 Motion::PreviousWord => {
1232 let line = self.lines.get(cursor.line)?;
1233 if cursor.index > 0 {
1234 cursor.index = line
1235 .text()
1236 .unicode_word_indices()
1237 .rev()
1238 .map(|(i, _)| i)
1239 .find(|&i| i < cursor.index)
1240 .unwrap_or(0);
1241 } else if cursor.line > 0 {
1242 cursor.line -= 1;
1243 cursor.index = self.lines.get(cursor.line)?.text().len();
1244 }
1245 cursor_x_opt = None;
1246 }
1247 Motion::NextWord => {
1248 let line = self.lines.get(cursor.line)?;
1249 if cursor.index < line.text().len() {
1250 cursor.index = line
1251 .text()
1252 .unicode_word_indices()
1253 .map(|(i, word)| i + word.len())
1254 .find(|&i| i > cursor.index)
1255 .unwrap_or(line.text().len());
1256 } else if cursor.line + 1 < self.lines.len() {
1257 cursor.line += 1;
1258 cursor.index = 0;
1259 }
1260 cursor_x_opt = None;
1261 }
1262 Motion::LeftWord => {
1263 let rtl_opt = self
1264 .line_shape(font_system, cursor.line)
1265 .map(|shape| shape.rtl);
1266 if let Some(rtl) = rtl_opt {
1267 if rtl {
1268 (cursor, cursor_x_opt) = self.cursor_motion(
1269 font_system,
1270 cursor,
1271 cursor_x_opt,
1272 Motion::NextWord,
1273 )?;
1274 } else {
1275 (cursor, cursor_x_opt) = self.cursor_motion(
1276 font_system,
1277 cursor,
1278 cursor_x_opt,
1279 Motion::PreviousWord,
1280 )?;
1281 }
1282 }
1283 }
1284 Motion::RightWord => {
1285 let rtl_opt = self
1286 .line_shape(font_system, cursor.line)
1287 .map(|shape| shape.rtl);
1288 if let Some(rtl) = rtl_opt {
1289 if rtl {
1290 (cursor, cursor_x_opt) = self.cursor_motion(
1291 font_system,
1292 cursor,
1293 cursor_x_opt,
1294 Motion::PreviousWord,
1295 )?;
1296 } else {
1297 (cursor, cursor_x_opt) = self.cursor_motion(
1298 font_system,
1299 cursor,
1300 cursor_x_opt,
1301 Motion::NextWord,
1302 )?;
1303 }
1304 }
1305 }
1306 Motion::BufferStart => {
1307 cursor.line = 0;
1308 cursor.index = 0;
1309 cursor_x_opt = None;
1310 }
1311 Motion::BufferEnd => {
1312 cursor.line = self.lines.len().saturating_sub(1);
1313 cursor.index = self.lines.get(cursor.line)?.text().len();
1314 cursor_x_opt = None;
1315 }
1316 Motion::GotoLine(line) => {
1317 let mut layout_cursor = self.layout_cursor(font_system, cursor)?;
1318 layout_cursor.line = line;
1319 (cursor, cursor_x_opt) = self.cursor_motion(
1320 font_system,
1321 cursor,
1322 cursor_x_opt,
1323 Motion::LayoutCursor(layout_cursor),
1324 )?;
1325 }
1326 }
1327 Some((cursor, cursor_x_opt))
1328 }
1329
1330 #[cfg(feature = "swash")]
1332 pub fn draw<F>(
1333 &self,
1334 font_system: &mut FontSystem,
1335 cache: &mut crate::SwashCache,
1336 color: Color,
1337 mut f: F,
1338 ) where
1339 F: FnMut(i32, i32, u32, u32, Color),
1340 {
1341 for run in self.layout_runs() {
1342 for glyph in run.glyphs.iter() {
1343 let physical_glyph = glyph.physical((0., 0.), 1.0);
1344
1345 let glyph_color = match glyph.color_opt {
1346 Some(some) => some,
1347 None => color,
1348 };
1349
1350 cache.with_pixels(
1351 font_system,
1352 physical_glyph.cache_key,
1353 glyph_color,
1354 |x, y, color| {
1355 f(
1356 physical_glyph.x + x,
1357 run.line_y as i32 + physical_glyph.y + y,
1358 1,
1359 1,
1360 color,
1361 );
1362 },
1363 );
1364 }
1365 }
1366 }
1367}
1368
1369impl BorrowedWithFontSystem<'_, Buffer> {
1370 pub fn shape_until_cursor(&mut self, cursor: Cursor, prune: bool) {
1372 self.inner
1373 .shape_until_cursor(self.font_system, cursor, prune);
1374 }
1375
1376 pub fn shape_until_scroll(&mut self, prune: bool) {
1378 self.inner.shape_until_scroll(self.font_system, prune);
1379 }
1380
1381 pub fn line_shape(&mut self, line_i: usize) -> Option<&ShapeLine> {
1383 self.inner.line_shape(self.font_system, line_i)
1384 }
1385
1386 pub fn line_layout(&mut self, line_i: usize) -> Option<&[LayoutLine]> {
1388 self.inner.line_layout(self.font_system, line_i)
1389 }
1390
1391 pub fn set_metrics(&mut self, metrics: Metrics) {
1397 self.inner.set_metrics(self.font_system, metrics);
1398 }
1399
1400 pub fn set_wrap(&mut self, wrap: Wrap) {
1402 self.inner.set_wrap(self.font_system, wrap);
1403 }
1404
1405 pub fn set_size(&mut self, width_opt: Option<f32>, height_opt: Option<f32>) {
1407 self.inner.set_size(self.font_system, width_opt, height_opt);
1408 }
1409
1410 pub fn set_metrics_and_size(
1416 &mut self,
1417 metrics: Metrics,
1418 width_opt: Option<f32>,
1419 height_opt: Option<f32>,
1420 ) {
1421 self.inner
1422 .set_metrics_and_size(self.font_system, metrics, width_opt, height_opt);
1423 }
1424
1425 pub fn set_tab_width(&mut self, tab_width: u16) {
1427 self.inner.set_tab_width(self.font_system, tab_width);
1428 }
1429
1430 pub fn set_text(&mut self, text: &str, attrs: Attrs, shaping: Shaping) {
1432 self.inner.set_text(self.font_system, text, attrs, shaping);
1433 }
1434
1435 pub fn set_rich_text<'r, 's, I>(
1454 &mut self,
1455 spans: I,
1456 default_attrs: Attrs,
1457 shaping: Shaping,
1458 alignment: Option<Align>,
1459 ) where
1460 I: IntoIterator<Item = (&'s str, Attrs<'r>)>,
1461 {
1462 self.inner
1463 .set_rich_text(self.font_system, spans, default_attrs, shaping, alignment);
1464 }
1465
1466 pub fn cursor_motion(
1468 &mut self,
1469 cursor: Cursor,
1470 cursor_x_opt: Option<i32>,
1471 motion: Motion,
1472 ) -> Option<(Cursor, Option<i32>)> {
1473 self.inner
1474 .cursor_motion(self.font_system, cursor, cursor_x_opt, motion)
1475 }
1476
1477 #[cfg(feature = "swash")]
1479 pub fn draw<F>(&mut self, cache: &mut crate::SwashCache, color: Color, f: F)
1480 where
1481 F: FnMut(i32, i32, u32, u32, Color),
1482 {
1483 self.inner.draw(self.font_system, cache, color, f);
1484 }
1485}