use eframe::egui; fn format_line_number(line_number: usize, line_side: bool, line_count_width: usize) -> String { if line_side { format!("{:width$}", line_number, width = line_count_width) } } pub(super) fn calculate_visual_line_mapping( ui: &egui::Ui, content: &str, available_width: f32, font_id: egui::FontId, ) -> Vec> { let mut visual_lines = Vec::new(); for (line_num, line) in content.lines().enumerate() { if line.is_empty() { visual_lines.push(Some(line_num + 1)); continue; } let galley = ui.fonts(|fonts| { fonts.layout( line.to_string(), font_id.to_owned(), egui::Color32::WHITE, available_width - font_id.size, ) }); let wrapped_line_count = galley.rows.len().max(1); visual_lines.push(Some(line_num + 1)); for _ in 1..wrapped_line_count { visual_lines.push(None); } } if content.ends_with('\n') && !content.is_empty() { let line_num = content.lines().count(); visual_lines.push(Some(line_num + 1)); } visual_lines } pub(super) fn render_line_numbers( ui: &mut egui::Ui, line_count: usize, visual_line_mapping: &[Option], line_number_width: f32, word_wrap: bool, line_side: bool, font_size: f32, ) { ui.vertical(|ui| { ui.disable(); ui.set_width(line_number_width); ui.spacing_mut().item_spacing.y = 0.0; ui.add_space(2.0); // Text Editor default top margin let text_color = ui.visuals().weak_text_color(); let bg_color = ui.visuals().extreme_bg_color; let line_numbers_rect = ui.available_rect_before_wrap(); ui.painter().rect_filled(line_numbers_rect, 0.0, bg_color); let font_id = egui::FontId::monospace(font_size); let line_count_width = line_count.to_string().len(); let line_texts = if word_wrap { visual_line_mapping .into_iter() .map(|line_number_opt| { line_number_opt.map_or_else( || " ".repeat(line_count_width), |line_number| format_line_number(line_number, line_side, line_count_width), ) }) .collect::>() } else { (1..=line_count) .map(|i| format_line_number(i, line_side, line_count_width)) .collect::>() }; for text in line_texts { ui.label( egui::RichText::new(text) .font(font_id.to_owned()) .color(text_color), ); } }); }