egui/text_selection/
visuals.rs1use std::sync::Arc;
2
3use crate::{Galley, Painter, Rect, Ui, Visuals, pos2, vec2};
4
5use super::CCursorRange;
6
7#[derive(Clone, Debug)]
8pub struct RowVertexIndices {
9 pub row: usize,
10 pub vertex_indices: [u32; 6],
11}
12
13pub fn paint_text_selection(
15 galley: &mut Arc<Galley>,
16 visuals: &Visuals,
17 cursor_range: &CCursorRange,
18 mut new_vertex_indices: Option<&mut Vec<RowVertexIndices>>,
19) {
20 if cursor_range.is_empty() {
21 return;
22 }
23
24 let galley: &mut Galley = Arc::make_mut(galley);
27
28 let background_color = visuals.selection.bg_fill;
29 let text_color = visuals.selection.stroke.color;
30
31 let [min, max] = cursor_range.sorted_cursors();
32 let min = galley.layout_from_cursor(min);
33 let max = galley.layout_from_cursor(max);
34
35 for ri in min.row..=max.row {
36 let placed_row = &mut galley.rows[ri];
37 let row = Arc::make_mut(&mut placed_row.row);
38
39 let left = if ri == min.row {
40 row.x_offset(min.column)
41 } else {
42 0.0
43 };
44 let right = if ri == max.row {
45 row.x_offset(max.column)
46 } else {
47 let newline_size = if placed_row.ends_with_newline {
48 row.height() / 2.0 } else {
50 0.0
51 };
52 row.size.x + newline_size
53 };
54
55 let rect = Rect::from_min_max(pos2(left, 0.0), pos2(right, row.size.y));
56 let mesh = &mut row.visuals.mesh;
57
58 if !row.glyphs.is_empty() {
59 let first_glyph_index = if ri == min.row { min.column } else { 0 };
61 let last_glyph_index = if ri == max.row {
62 max.column
63 } else {
64 row.glyphs.len() - 1
65 };
66
67 let first_vertex_index = row
68 .glyphs
69 .get(first_glyph_index)
70 .map_or(row.visuals.glyph_vertex_range.end, |g| g.first_vertex as _);
71 let last_vertex_index = row
72 .glyphs
73 .get(last_glyph_index)
74 .map_or(row.visuals.glyph_vertex_range.end, |g| g.first_vertex as _);
75
76 for vi in first_vertex_index..last_vertex_index {
77 mesh.vertices[vi].color = text_color;
78 }
79 }
80
81 let glyph_index_start = row.visuals.glyph_index_start;
85
86 let num_indices_before = mesh.indices.len();
88 mesh.add_colored_rect(rect, background_color);
89 assert_eq!(
90 num_indices_before + 6,
91 mesh.indices.len(),
92 "We expect exactly 6 new indices"
93 );
94
95 let selection_triangles = [
97 mesh.indices[num_indices_before],
98 mesh.indices[num_indices_before + 1],
99 mesh.indices[num_indices_before + 2],
100 mesh.indices[num_indices_before + 3],
101 mesh.indices[num_indices_before + 4],
102 mesh.indices[num_indices_before + 5],
103 ];
104
105 for i in (glyph_index_start..num_indices_before).rev() {
107 mesh.indices.swap(i, i + 6);
108 }
109 mesh.indices[glyph_index_start..glyph_index_start + 6]
111 .clone_from_slice(&selection_triangles);
112
113 row.visuals.mesh_bounds = mesh.calc_bounds();
114
115 if let Some(new_vertex_indices) = &mut new_vertex_indices {
116 new_vertex_indices.push(RowVertexIndices {
117 row: ri,
118 vertex_indices: selection_triangles,
119 });
120 }
121 }
122}
123
124pub fn paint_cursor_end(painter: &Painter, visuals: &Visuals, cursor_rect: Rect) {
128 let stroke = visuals.text_cursor.stroke;
129
130 let top = cursor_rect.center_top();
131 let bottom = cursor_rect.center_bottom();
132
133 painter.line_segment([top, bottom], (stroke.width, stroke.color));
134
135 if false {
136 let extrusion = 3.0;
138 let width = 1.0;
139 painter.line_segment(
140 [top - vec2(extrusion, 0.0), top + vec2(extrusion, 0.0)],
141 (width, stroke.color),
142 );
143 painter.line_segment(
144 [bottom - vec2(extrusion, 0.0), bottom + vec2(extrusion, 0.0)],
145 (width, stroke.color),
146 );
147 }
148}
149
150pub fn paint_text_cursor(
152 ui: &Ui,
153 painter: &Painter,
154 primary_cursor_rect: Rect,
155 time_since_last_interaction: f64,
156) {
157 if ui.visuals().text_cursor.blink {
158 let on_duration = ui.visuals().text_cursor.on_duration;
159 let off_duration = ui.visuals().text_cursor.off_duration;
160 let total_duration = on_duration + off_duration;
161
162 let time_in_cycle = (time_since_last_interaction % (total_duration as f64)) as f32;
163
164 let wake_in = if time_in_cycle < on_duration {
165 paint_cursor_end(painter, ui.visuals(), primary_cursor_rect);
167 on_duration - time_in_cycle
168 } else {
169 total_duration - time_in_cycle
171 };
172
173 ui.request_repaint_after_secs(wake_in);
174 } else {
175 paint_cursor_end(painter, ui.visuals(), primary_cursor_rect);
176 }
177}