egui/text_selection/
visuals.rs

1use crate::*;
2
3use self::layers::ShapeIdx;
4
5use super::CursorRange;
6
7pub fn paint_text_selection(
8    painter: &Painter,
9    visuals: &Visuals,
10    galley_pos: Pos2,
11    galley: &Galley,
12    cursor_range: &CursorRange,
13    mut out_shaped_idx: Option<&mut Vec<ShapeIdx>>,
14) {
15    if cursor_range.is_empty() {
16        return;
17    }
18
19    // We paint the cursor selection on top of the text, so make it transparent:
20    let color = visuals.selection.bg_fill.linear_multiply(0.5);
21    let [min, max] = cursor_range.sorted_cursors();
22    let min = min.rcursor;
23    let max = max.rcursor;
24
25    for ri in min.row..=max.row {
26        let row = &galley.rows[ri];
27        let left = if ri == min.row {
28            row.x_offset(min.column)
29        } else {
30            row.rect.left()
31        };
32        let right = if ri == max.row {
33            row.x_offset(max.column)
34        } else {
35            let newline_size = if row.ends_with_newline {
36                row.height() / 2.0 // visualize that we select the newline
37            } else {
38                0.0
39            };
40            row.rect.right() + newline_size
41        };
42        let rect = Rect::from_min_max(
43            galley_pos + vec2(left, row.min_y()),
44            galley_pos + vec2(right, row.max_y()),
45        );
46        let shape_idx = painter.rect_filled(rect, 0.0, color);
47        if let Some(out_shaped_idx) = &mut out_shaped_idx {
48            out_shaped_idx.push(shape_idx);
49        }
50    }
51}
52
53/// Paint one end of the selection, e.g. the primary cursor.
54///
55/// This will never blink.
56pub fn paint_cursor_end(painter: &Painter, visuals: &Visuals, cursor_rect: Rect) {
57    let stroke = visuals.text_cursor.stroke;
58
59    let top = cursor_rect.center_top();
60    let bottom = cursor_rect.center_bottom();
61
62    painter.line_segment([top, bottom], (stroke.width, stroke.color));
63
64    if false {
65        // Roof/floor:
66        let extrusion = 3.0;
67        let width = 1.0;
68        painter.line_segment(
69            [top - vec2(extrusion, 0.0), top + vec2(extrusion, 0.0)],
70            (width, stroke.color),
71        );
72        painter.line_segment(
73            [bottom - vec2(extrusion, 0.0), bottom + vec2(extrusion, 0.0)],
74            (width, stroke.color),
75        );
76    }
77}
78
79/// Paint one end of the selection, e.g. the primary cursor, with blinking (if enabled).
80pub fn paint_text_cursor(
81    ui: &Ui,
82    painter: &Painter,
83    primary_cursor_rect: Rect,
84    time_since_last_edit: f64,
85) {
86    if ui.visuals().text_cursor.blink {
87        let on_duration = ui.visuals().text_cursor.on_duration;
88        let off_duration = ui.visuals().text_cursor.off_duration;
89        let total_duration = on_duration + off_duration;
90
91        let time_in_cycle = (time_since_last_edit % (total_duration as f64)) as f32;
92
93        let wake_in = if time_in_cycle < on_duration {
94            // Cursor is visible
95            paint_cursor_end(painter, ui.visuals(), primary_cursor_rect);
96            on_duration - time_in_cycle
97        } else {
98            // Cursor is not visible
99            total_duration - time_in_cycle
100        };
101
102        ui.ctx().request_repaint_after_secs(wake_in);
103    } else {
104        paint_cursor_end(painter, ui.visuals(), primary_cursor_rect);
105    }
106}