2025-07-05 14:42:45 -04:00
|
|
|
mod editor;
|
|
|
|
|
mod find_highlight;
|
|
|
|
|
mod line_numbers;
|
|
|
|
|
|
|
|
|
|
use crate::app::TextEditor;
|
|
|
|
|
use eframe::egui;
|
|
|
|
|
|
|
|
|
|
use self::editor::editor_view_ui;
|
|
|
|
|
use self::line_numbers::{get_visual_line_mapping, render_line_numbers};
|
|
|
|
|
|
|
|
|
|
pub(crate) fn central_panel(app: &mut TextEditor, ctx: &egui::Context) {
|
|
|
|
|
let show_line_numbers = app.show_line_numbers;
|
|
|
|
|
let word_wrap = app.word_wrap;
|
|
|
|
|
let line_side = app.line_side;
|
|
|
|
|
let font_size = app.font_size;
|
|
|
|
|
|
2025-07-15 00:42:01 -04:00
|
|
|
let output = egui::CentralPanel::default()
|
2025-07-05 14:42:45 -04:00
|
|
|
.frame(egui::Frame::NONE)
|
|
|
|
|
.show(ctx, |ui| {
|
|
|
|
|
let bg_color = ui.visuals().extreme_bg_color;
|
|
|
|
|
let panel_rect = ui.available_rect_before_wrap();
|
|
|
|
|
ui.painter().rect_filled(panel_rect, 0.0, bg_color);
|
|
|
|
|
let editor_height = panel_rect.height();
|
|
|
|
|
|
|
|
|
|
if !show_line_numbers || app.get_active_tab().is_none() {
|
|
|
|
|
egui::ScrollArea::vertical()
|
|
|
|
|
.auto_shrink([false; 2])
|
|
|
|
|
.show(ui, |ui| {
|
|
|
|
|
editor_view_ui(ui, app);
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let line_count = app.get_text_processing_result().line_count;
|
|
|
|
|
let editor_dimensions = app.calculate_editor_dimensions(ui);
|
|
|
|
|
let line_number_width = editor_dimensions.line_number_width;
|
|
|
|
|
|
|
|
|
|
let visual_line_mapping = if word_wrap {
|
|
|
|
|
let available_text_width = editor_dimensions.text_width;
|
|
|
|
|
if let Some(active_tab) = app.get_active_tab() {
|
|
|
|
|
get_visual_line_mapping(
|
|
|
|
|
ui,
|
|
|
|
|
&active_tab.content,
|
|
|
|
|
available_text_width,
|
|
|
|
|
font_size,
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
vec![]
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
vec![]
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let line_numbers_widget = |ui: &mut egui::Ui| {
|
|
|
|
|
render_line_numbers(
|
|
|
|
|
ui,
|
|
|
|
|
line_count,
|
|
|
|
|
&visual_line_mapping,
|
|
|
|
|
line_number_width,
|
|
|
|
|
word_wrap,
|
|
|
|
|
font_size,
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let separator_widget = |ui: &mut egui::Ui| {
|
|
|
|
|
ui.add_space(3.0);
|
|
|
|
|
let separator_x = ui.cursor().left();
|
|
|
|
|
let mut y_range = ui.available_rect_before_wrap().y_range();
|
|
|
|
|
y_range.max += 2.0 * font_size; // Extend separator to cover more vertical space
|
|
|
|
|
ui.painter()
|
|
|
|
|
.vline(separator_x, y_range, ui.visuals().window_stroke);
|
|
|
|
|
ui.add_space(4.0);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
egui::ScrollArea::vertical()
|
|
|
|
|
.auto_shrink([false; 2])
|
|
|
|
|
.show(ui, |ui| {
|
|
|
|
|
if line_side {
|
|
|
|
|
// Line numbers on the right
|
|
|
|
|
let text_editor_width = editor_dimensions.text_width + editor_dimensions.total_reserved_width;
|
|
|
|
|
ui.allocate_ui_with_layout(
|
|
|
|
|
egui::vec2(text_editor_width, editor_height),
|
|
|
|
|
egui::Layout::left_to_right(egui::Align::TOP),
|
|
|
|
|
|ui| {
|
|
|
|
|
// Constrain editor to specific width to leave space for line numbers
|
|
|
|
|
ui.allocate_ui_with_layout(
|
|
|
|
|
egui::vec2(editor_dimensions.text_width, editor_height),
|
|
|
|
|
egui::Layout::left_to_right(egui::Align::TOP),
|
|
|
|
|
|ui| {
|
|
|
|
|
editor_view_ui(ui, app);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
separator_widget(ui);
|
|
|
|
|
line_numbers_widget(ui);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
// Line numbers on the left
|
|
|
|
|
let text_editor_width = editor_dimensions.text_width + editor_dimensions.total_reserved_width;
|
|
|
|
|
ui.allocate_ui_with_layout(
|
|
|
|
|
egui::vec2(text_editor_width, editor_height),
|
|
|
|
|
egui::Layout::left_to_right(egui::Align::TOP),
|
|
|
|
|
|ui| {
|
|
|
|
|
line_numbers_widget(ui);
|
|
|
|
|
separator_widget(ui);
|
|
|
|
|
editor_view_ui(ui, app);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
2025-07-15 00:42:01 -04:00
|
|
|
|
|
|
|
|
output.response.context_menu(|ui| {
|
|
|
|
|
let text_len = app.get_active_tab().unwrap().content.len();
|
|
|
|
|
let reset_zoom_key = egui::Id::new("editor_reset_zoom");
|
|
|
|
|
|
|
|
|
|
if ui.button("Cut").clicked() {
|
|
|
|
|
ui.ctx().send_viewport_cmd(egui::ViewportCommand::RequestCut);
|
|
|
|
|
ui.close_menu();
|
|
|
|
|
}
|
|
|
|
|
if ui.button("Copy").clicked() {
|
|
|
|
|
ui.ctx().send_viewport_cmd(egui::ViewportCommand::RequestCopy);
|
|
|
|
|
ui.close_menu();
|
|
|
|
|
}
|
|
|
|
|
if ui.button("Paste").clicked() {
|
|
|
|
|
ui.ctx()
|
|
|
|
|
.send_viewport_cmd(egui::ViewportCommand::RequestPaste);
|
|
|
|
|
ui.close_menu();
|
|
|
|
|
}
|
|
|
|
|
if ui.button("Delete").clicked() {
|
|
|
|
|
ui.ctx().input_mut(|i| {
|
|
|
|
|
i.events.push(egui::Event::Key {
|
|
|
|
|
key: egui::Key::Delete,
|
|
|
|
|
physical_key: None,
|
|
|
|
|
pressed: true,
|
|
|
|
|
repeat: false,
|
|
|
|
|
modifiers: egui::Modifiers::NONE,
|
|
|
|
|
})
|
|
|
|
|
});
|
|
|
|
|
ui.close_menu();
|
|
|
|
|
}
|
|
|
|
|
if ui.button("Select All").clicked() {
|
|
|
|
|
let text_edit_id = egui::Id::new("main_text_editor");
|
|
|
|
|
if let Some(mut state) = egui::TextEdit::load_state(ui.ctx(), text_edit_id) {
|
|
|
|
|
let select_all_range = egui::text::CCursorRange::two(
|
|
|
|
|
egui::text::CCursor::new(0),
|
|
|
|
|
egui::text::CCursor::new(text_len),
|
|
|
|
|
);
|
|
|
|
|
state.cursor.set_char_range(Some(select_all_range));
|
|
|
|
|
egui::TextEdit::store_state(ui.ctx(), text_edit_id, state);
|
|
|
|
|
}
|
|
|
|
|
ui.close_menu();
|
|
|
|
|
}
|
|
|
|
|
ui.separator();
|
|
|
|
|
if ui.button("Reset Zoom").clicked() {
|
|
|
|
|
ui.ctx().memory_mut(|mem| {
|
|
|
|
|
mem.data.insert_temp(reset_zoom_key, true);
|
|
|
|
|
});
|
|
|
|
|
ui.close_menu();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2025-07-05 14:42:45 -04:00
|
|
|
}
|