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; let _output = egui::CentralPanel::default() .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() { let _scroll_response = egui::ScrollArea::vertical() .auto_shrink([false; 2]) .show(ui, |ui| { let full_rect = ui.available_rect_before_wrap(); let context_response = ui.allocate_response(full_rect.size(), egui::Sense::click()); ui.scope_builder(egui::UiBuilder::new().max_rect(full_rect), |ui| { editor_view_ui(ui, app); }); show_context_menu(ui, app, &context_response); }); 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| { // Create an invisible interaction area for context menu let full_rect = ui.available_rect_before_wrap(); let context_response = ui.allocate_response( full_rect.size(), egui::Sense::click(), ); // Reset cursor to render editor at the top ui.scope_builder( egui::UiBuilder::new().max_rect(full_rect), |ui| { editor_view_ui(ui, app); }, ); show_context_menu(ui, app, &context_response); }, ); 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); // Create an invisible interaction area for context menu let editor_area = ui.available_rect_before_wrap(); let context_response = ui.allocate_response(editor_area.size(), egui::Sense::click()); // Reset cursor to render editor at the current position ui.scope_builder( egui::UiBuilder::new().max_rect(editor_area), |ui| { editor_view_ui(ui, app); }, ); show_context_menu(ui, app, &context_response); }, ); } }); }); } fn show_context_menu(_ui: &mut egui::Ui, app: &mut TextEditor, context_response: &egui::Response) { context_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(); } }); }