2025-07-05 14:42:45 -04:00
|
|
|
use eframe::egui;
|
|
|
|
|
|
2025-07-28 23:57:36 -04:00
|
|
|
fn format_line_number(line_number: usize, line_side: bool, line_count_width: usize) -> String {
|
|
|
|
|
if line_side {
|
2025-12-14 19:38:57 -05:00
|
|
|
// Right side: left-align with trailing space for scrollbar clearance
|
|
|
|
|
format!("{:<width$} ", line_number, width = line_count_width)
|
2025-07-28 23:57:36 -04:00
|
|
|
} else {
|
2025-12-14 19:38:57 -05:00
|
|
|
// Left side: right-align, no trailing space (separator provides gap)
|
2025-07-28 23:57:36 -04:00
|
|
|
format!("{:>width$}", line_number, width = line_count_width)
|
2025-07-05 14:42:45 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-28 23:57:36 -04:00
|
|
|
pub(super) fn calculate_visual_line_mapping(
|
2025-07-05 14:42:45 -04:00
|
|
|
ui: &egui::Ui,
|
|
|
|
|
content: &str,
|
|
|
|
|
available_width: f32,
|
2025-07-28 23:57:36 -04:00
|
|
|
font_id: egui::FontId,
|
2025-07-05 14:42:45 -04:00
|
|
|
) -> 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;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-14 19:38:57 -05:00
|
|
|
let galley = ui.fonts_mut(|fonts| {
|
2025-07-05 14:42:45 -04:00
|
|
|
fonts.layout(
|
|
|
|
|
line.to_string(),
|
2025-07-16 17:20:09 -04:00
|
|
|
font_id.to_owned(),
|
2025-07-05 14:42:45 -04:00
|
|
|
egui::Color32::WHITE,
|
2025-12-14 19:38:57 -05:00
|
|
|
available_width,
|
2025-07-05 14:42:45 -04:00
|
|
|
)
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-28 23:57:36 -04:00
|
|
|
if content.ends_with('\n') && !content.is_empty() {
|
|
|
|
|
let line_num = content.lines().count();
|
|
|
|
|
visual_lines.push(Some(line_num + 1));
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-05 14:42:45 -04:00
|
|
|
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,
|
2025-07-28 23:57:36 -04:00
|
|
|
line_side: bool,
|
2025-07-05 14:42:45 -04:00
|
|
|
font_size: f32,
|
|
|
|
|
) {
|
|
|
|
|
ui.vertical(|ui| {
|
2025-07-28 23:57:36 -04:00
|
|
|
ui.disable();
|
2025-07-05 14:42:45 -04:00
|
|
|
ui.set_width(line_number_width);
|
|
|
|
|
ui.spacing_mut().item_spacing.y = 0.0;
|
2025-12-14 19:38:57 -05:00
|
|
|
ui.add_space(1.0); // Text Editor default top margin
|
2025-07-05 14:42:45 -04:00
|
|
|
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();
|
2025-07-15 11:07:41 -04:00
|
|
|
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();
|
|
|
|
|
|
2025-07-28 23:57:36 -04:00
|
|
|
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<_>>()
|
2025-07-05 14:42:45 -04:00
|
|
|
} else {
|
2025-07-28 23:57:36 -04:00
|
|
|
(1..=line_count)
|
|
|
|
|
.map(|i| format_line_number(i, line_side, line_count_width))
|
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
|
};
|
2025-12-14 19:38:57 -05:00
|
|
|
|
2025-07-28 23:57:36 -04:00
|
|
|
for text in line_texts {
|
|
|
|
|
ui.label(
|
|
|
|
|
egui::RichText::new(text)
|
|
|
|
|
.font(font_id.to_owned())
|
|
|
|
|
.color(text_color),
|
|
|
|
|
);
|
2025-07-05 14:42:45 -04:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|