ced/src/ui/central_panel/line_numbers.rs

119 lines
3.4 KiB
Rust
Raw Normal View History

2025-07-05 14:42:45 -04:00
use eframe::egui;
thread_local! {
static VISUAL_LINE_MAPPING_CACHE: std::cell::RefCell<Option<(String, f32, Vec<Option<usize>>)>> = std::cell::RefCell::new(None);
}
pub(super) fn get_visual_line_mapping(
ui: &egui::Ui,
content: &str,
available_width: f32,
font_size: f32,
) -> Vec<Option<usize>> {
let should_recalculate = VISUAL_LINE_MAPPING_CACHE.with(|cache| {
if let Some((cached_content, cached_width, _)) = cache.borrow().as_ref() {
content != cached_content || available_width != *cached_width
} else {
true
}
});
if should_recalculate {
let visual_lines = calculate_visual_line_mapping(ui, content, available_width, font_size);
VISUAL_LINE_MAPPING_CACHE.with(|cache| {
*cache.borrow_mut() = Some((content.to_owned(), available_width, visual_lines));
});
}
VISUAL_LINE_MAPPING_CACHE.with(|cache| {
cache
.borrow()
.as_ref()
.map(|(_, _, mapping)| mapping.clone())
.unwrap_or_default()
})
}
fn calculate_visual_line_mapping(
ui: &egui::Ui,
content: &str,
available_width: f32,
font_size: f32,
) -> Vec<Option<usize>> {
let mut visual_lines = Vec::new();
let font_id = egui::FontId::monospace(font_size);
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.clone(),
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);
}
}
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,
font_size: f32,
) {
ui.vertical(|ui| {
ui.set_width(line_number_width);
ui.spacing_mut().item_spacing.y = 0.0;
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);
2025-07-05 14:42:45 -04:00
let font_id = egui::FontId::monospace(font_size);
let line_count_width = line_count.to_string().len();
if word_wrap {
for line_number_opt in visual_line_mapping {
let text = if let Some(line_number) = line_number_opt {
format!("{:>width$}", line_number, width = line_count_width)
} else {
" ".repeat(line_count_width)
};
ui.label(
egui::RichText::new(text)
.font(font_id.clone())
.color(text_color),
);
}
} else {
for i in 1..=line_count {
let text = format!("{:>width$}", i, width = line_count_width);
ui.label(
egui::RichText::new(text)
.font(font_id.clone())
.color(text_color),
);
}
}
});
}