use crate::app::TextEditor; use eframe::egui; use egui_extras::syntax_highlighting::{self}; use super::find_highlight; pub(super) fn editor_view_ui(ui: &mut egui::Ui, app: &mut TextEditor) -> egui::Response { let _current_match_position = app.get_current_match_position(); let show_find = app.show_find; let _prev_show_find = app.prev_show_find; let show_preferences = app.show_preferences; let show_about = app.show_about; let show_shortcuts = app.show_shortcuts; let word_wrap = app.word_wrap; let font_size = app.font_size; let font_id = app.get_font_id(); let syntax_highlighting_enabled = app.syntax_highlighting; let reset_zoom_key = egui::Id::new("editor_reset_zoom"); let should_reset_zoom = ui .ctx() .memory_mut(|mem| mem.data.get_temp::(reset_zoom_key).unwrap_or(false)); if should_reset_zoom { app.zoom_factor = 1.0; ui.ctx().set_zoom_factor(1.0); ui.ctx().memory_mut(|mem| { mem.data.insert_temp(reset_zoom_key, false); }); } let estimated_width = if !word_wrap { app.calculate_content_based_width(ui) } else { 0.0 }; let find_data = if show_find && !app.find_matches.is_empty() { app.get_active_tab().map(|tab| { ( tab.content.to_owned(), app.find_matches.to_owned(), app.current_match_index, ) }) } else { None }; let Some(active_tab) = app.get_active_tab_mut() else { return ui.label("No file open, how did you get here?"); }; let bg_color = ui.visuals().extreme_bg_color; let editor_rect = ui.available_rect_before_wrap(); ui.painter().rect_filled(editor_rect, 0.0, bg_color); if let Some((content, matches, current_match_index)) = &find_data { let font_id = ui .style() .text_styles .get(&egui::TextStyle::Monospace) .unwrap_or(&egui::FontId::monospace(font_size)) .to_owned(); let desired_width = if word_wrap { ui.available_width() } else { f32::INFINITY }; let temp_galley = ui.fonts(|fonts| { fonts.layout( content.to_owned(), font_id.to_owned(), ui.visuals().text_color(), desired_width, ) }); let text_area_left = editor_rect.left() + 4.0; let text_area_top = editor_rect.top() + 2.0; find_highlight::draw_find_highlights( ui, content, matches, *current_match_index, &temp_galley, text_area_left, text_area_top, font_size, ); } let desired_width = if word_wrap { ui.available_width() } else { f32::INFINITY }; let language = super::languages::get_language_from_extension(active_tab.file_path.as_deref()); let mut layouter = |ui: &egui::Ui, string: &dyn egui::TextBuffer, wrap_width: f32| { // let syntect_theme = // crate::app::theme::create_code_theme_from_visuals(ui.visuals(), font_size); let text = string.as_str(); let theme = egui_extras::syntax_highlighting::CodeTheme::dark(font_size); let mut layout_job = if syntax_highlighting_enabled && language != "txt" { // let mut settings = egui_extras::syntax_highlighting::SyntectSettings::default(); // settings.ts = syntect_theme; // syntax_highlighting::highlight_with(ui.ctx(), &ui.style().clone(), &theme, text, &language, &settings) syntax_highlighting::highlight(ui.ctx(), &ui.style().clone(), &theme, text, &language) } else { syntax_highlighting::highlight(ui.ctx(), &ui.style().clone(), &theme, text, "") }; if syntax_highlighting_enabled && language != "txt" { for section in &mut layout_job.sections { section.format.font_id = font_id.clone(); } } layout_job.wrap.max_width = wrap_width; ui.fonts(|f| f.layout_job(layout_job)) }; let text_edit = egui::TextEdit::multiline(&mut active_tab.content) .frame(false) .code_editor() .desired_width(desired_width) .desired_rows(0) .lock_focus(!show_find) .cursor_at_end(false) .layouter(&mut layouter) .id(egui::Id::new("main_text_editor")); let output = if word_wrap { text_edit.show(ui) } else { egui::ScrollArea::horizontal() .auto_shrink([false; 2]) .show(ui, |ui| { ui.allocate_ui_with_layout( egui::Vec2::new(estimated_width, ui.available_height()), egui::Layout::left_to_right(egui::Align::TOP), |ui| text_edit.show(ui), ) }) .inner .inner }; let content_changed = output.response.changed(); let content_for_processing = if content_changed { active_tab.update_modified_state(); Some(active_tab.content.to_owned()) } else { None }; // Save state cache when content changes (after releasing the borrow) if content_changed { if let Err(e) = app.save_state_cache() { eprintln!("Failed to save state cache: {e}"); } } if content_changed && app.show_find && !app.find_query.is_empty() { app.update_find_matches(); } let current_cursor_pos = output .state .cursor .char_range() .map(|range| range.primary.index); if let Some(content) = content_for_processing { let previous_content = app.previous_content.to_owned(); let previous_cursor_pos = app.previous_cursor_char_index; if !previous_content.is_empty() { if let (Some(prev_cursor_pos), Some(curr_cursor_pos)) = (previous_cursor_pos, current_cursor_pos) { app.process_incremental_change( &previous_content, &content, prev_cursor_pos, curr_cursor_pos, ui, ); } } else { app.process_text_for_rendering(&content, ui); } app.previous_content = content.to_owned(); app.previous_cursor_char_index = current_cursor_pos; } if app.font_settings_changed || app.text_needs_processing { if let Some(active_tab) = app.get_active_tab() { let content = active_tab.content.to_owned(); app.process_text_for_rendering(&content, ui); } app.font_settings_changed = false; app.text_needs_processing = false; } if !word_wrap { if let Some(cursor_pos) = current_cursor_pos { let cursor_moved = Some(cursor_pos) != app.previous_cursor_position; let text_changed = output.response.changed(); if cursor_moved || text_changed { if let Some(active_tab) = app.get_active_tab() { let content = &active_tab.content; let cursor_line = content .char_indices() .take_while(|(byte_pos, _)| *byte_pos < cursor_pos) .filter(|(_, ch)| *ch == '\n') .count(); let font_id = ui .style() .text_styles .get(&egui::TextStyle::Monospace) .unwrap_or(&egui::FontId::monospace(font_size)) .to_owned(); let line_height = ui.fonts(|fonts| fonts.row_height(&font_id)); let y_pos = output.response.rect.top() + (cursor_line as f32 * line_height); let cursor_rect = egui::Rect::from_min_size( egui::pos2(output.response.rect.left(), y_pos), egui::vec2(2.0, line_height), ); let visible_area = ui.clip_rect(); if !visible_area.intersects(cursor_rect) { ui.scroll_to_rect(cursor_rect, Some(egui::Align::Center)); } } } app.previous_cursor_position = Some(cursor_pos); } } if !output.response.has_focus() && !show_preferences && !show_about && !show_shortcuts && !show_find { output.response.request_focus(); } output.response }