ced/src/ui/central_panel/line_numbers.rs
2025-12-14 19:38:57 -05:00

100 lines
3.0 KiB
Rust

use eframe::egui;
fn format_line_number(line_number: usize, line_side: bool, line_count_width: usize) -> String {
if line_side {
// Right side: left-align with trailing space for scrollbar clearance
format!("{:<width$} ", line_number, width = line_count_width)
} else {
// Left side: right-align, no trailing space (separator provides gap)
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<Option<usize>> {
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_mut(|fonts| {
fonts.layout(
line.to_string(),
font_id.to_owned(),
egui::Color32::WHITE,
available_width,
)
});
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<usize>],
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(1.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::<Vec<_>>()
} else {
(1..=line_count)
.map(|i| format_line_number(i, line_side, line_count_width))
.collect::<Vec<_>>()
};
for text in line_texts {
ui.label(
egui::RichText::new(text)
.font(font_id.to_owned())
.color(text_color),
);
}
});
}