1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
use femtovg::{Align, Baseline, Canvas, Paint, Path, Renderer};

pub struct CursorSettings {
    pub width: f32,
    pub index: usize,
}

pub fn render_text<R: Renderer>(
    text: &str,
    x: f32,
    y: f32,
    text_width: f32,
    text_height: f32,
    paint: &Paint,
    cursor: Option<CursorSettings>,
    max_lines: Option<usize>,
    canvas: &mut Canvas<R>,
) -> usize {
    let Ok(split_text) = canvas.break_text_vec(text_width, text, paint) else {
        return 0;
    };

    let mut count = split_text.len();
    if let Some(line_count) = max_lines {
        if line_count < count {
            count = line_count;
        }
    }
    let offset_ratio = match paint.text_baseline() {
        Baseline::Top => 0.0,
        Baseline::Bottom => 1.0,
        _ => 0.5,
    };

    for (i, range) in split_text.into_iter().take(count).enumerate() {
        let y = y + text_height * (i as f32 - (count - 1) as f32 * offset_ratio);

        let _ = canvas.fill_text(x, y, &text[range.clone()], paint);

        let Some(CursorSettings {
            width: cursor_width,
            index: text_cursor,
        }) = cursor else {
            continue;
        };

        if !range.contains(&text_cursor) && (text.len() != range.end || text_cursor != range.end) {
            continue;
        }

        let row_cursor = text_cursor - range.start;

        let text = &text[range];

        let (Ok(metrics), Ok(full_metrics)) = (
            canvas.measure_text(x, y, &text[0..row_cursor], paint),
            canvas.measure_text(x, y, text, paint),
        ) else {
            continue;
        };

        let cursor_ratio = match paint.text_align() {
            Align::Left => 0.0,
            Align::Center => 0.5,
            Align::Right => 1.0,
        };
        let x = x + metrics.width() - full_metrics.width() * cursor_ratio;
        let y = y - text_height * offset_ratio;
        let mut path = Path::new();
        path.move_to(x, y);
        path.line_to(x, y + text_height);
        let mut cursor_paint = paint.clone();
        cursor_paint.set_line_width(cursor_width);
        canvas.stroke_path(&mut path, &cursor_paint);
    }

    count
}