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 color = visuals.selection.bg_fill;
29 let [min, max] = cursor_range.sorted_cursors();
30 let min = galley.layout_from_cursor(min);
31 let max = galley.layout_from_cursor(max);
32
33 for ri in min.row..=max.row {
34 let row = Arc::make_mut(&mut galley.rows[ri].row);
35
36 let left = if ri == min.row {
37 row.x_offset(min.column)
38 } else {
39 0.0
40 };
41 let right = if ri == max.row {
42 row.x_offset(max.column)
43 } else {
44 let newline_size = if row.ends_with_newline {
45 row.height() / 2.0 } else {
47 0.0
48 };
49 row.size.x + newline_size
50 };
51
52 let rect = Rect::from_min_max(pos2(left, 0.0), pos2(right, row.size.y));
53 let mesh = &mut row.visuals.mesh;
54
55 let glyph_index_start = row.visuals.glyph_index_start;
59
60 let num_indices_before = mesh.indices.len();
62 mesh.add_colored_rect(rect, color);
63 assert_eq!(
64 num_indices_before + 6,
65 mesh.indices.len(),
66 "We expect exactly 6 new indices"
67 );
68
69 let selection_triangles = [
71 mesh.indices[num_indices_before],
72 mesh.indices[num_indices_before + 1],
73 mesh.indices[num_indices_before + 2],
74 mesh.indices[num_indices_before + 3],
75 mesh.indices[num_indices_before + 4],
76 mesh.indices[num_indices_before + 5],
77 ];
78
79 for i in (glyph_index_start..num_indices_before).rev() {
81 mesh.indices.swap(i, i + 6);
82 }
83 mesh.indices[glyph_index_start..glyph_index_start + 6]
85 .clone_from_slice(&selection_triangles);
86
87 row.visuals.mesh_bounds = mesh.calc_bounds();
88
89 if let Some(new_vertex_indices) = &mut new_vertex_indices {
90 new_vertex_indices.push(RowVertexIndices {
91 row: ri,
92 vertex_indices: selection_triangles,
93 });
94 }
95 }
96}
97
98pub fn paint_cursor_end(painter: &Painter, visuals: &Visuals, cursor_rect: Rect) {
102 let stroke = visuals.text_cursor.stroke;
103
104 let top = cursor_rect.center_top();
105 let bottom = cursor_rect.center_bottom();
106
107 painter.line_segment([top, bottom], (stroke.width, stroke.color));
108
109 if false {
110 let extrusion = 3.0;
112 let width = 1.0;
113 painter.line_segment(
114 [top - vec2(extrusion, 0.0), top + vec2(extrusion, 0.0)],
115 (width, stroke.color),
116 );
117 painter.line_segment(
118 [bottom - vec2(extrusion, 0.0), bottom + vec2(extrusion, 0.0)],
119 (width, stroke.color),
120 );
121 }
122}
123
124pub fn paint_text_cursor(
126 ui: &Ui,
127 painter: &Painter,
128 primary_cursor_rect: Rect,
129 time_since_last_interaction: f64,
130) {
131 if ui.visuals().text_cursor.blink {
132 let on_duration = ui.visuals().text_cursor.on_duration;
133 let off_duration = ui.visuals().text_cursor.off_duration;
134 let total_duration = on_duration + off_duration;
135
136 let time_in_cycle = (time_since_last_interaction % (total_duration as f64)) as f32;
137
138 let wake_in = if time_in_cycle < on_duration {
139 paint_cursor_end(painter, ui.visuals(), primary_cursor_rect);
141 on_duration - time_in_cycle
142 } else {
143 total_duration - time_in_cycle
145 };
146
147 ui.ctx().request_repaint_after_secs(wake_in);
148 } else {
149 paint_cursor_end(painter, ui.visuals(), primary_cursor_rect);
150 }
151}