init, basic glogg clone

This commit is contained in:
2025-12-02 18:30:12 +01:00
commit a53ced9160
26 changed files with 6512 additions and 0 deletions

275
src/main.rs Normal file
View File

@@ -0,0 +1,275 @@
mod config;
mod file_tab;
mod highlight;
mod line_index;
mod search;
mod tab_manager;
mod types;
mod ui;
use eframe::egui;
use std::sync::Arc;
use config::AppConfig;
use file_tab::FileTab;
use highlight::HighlightManager;
use search::{add_to_history, start_search, SearchParams, SearchState};
use tab_manager::{close_tab, open_file_dialog, IndexingState};
use ui::{
render_highlight_editor, render_log_view, render_search_panel, render_tabs_panel,
render_top_menu, LogViewContext, SearchPanelState,
};
fn main() -> eframe::Result {
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
.with_inner_size([1200.0, 800.0])
.with_min_inner_size([800.0, 600.0])
.with_maximized(true)
.with_title("RLogg - Log Viewer"),
..Default::default()
};
let config = AppConfig::load();
eframe::run_native(
"RLogg",
options,
Box::new(move |_cc| Ok(Box::new(LogViewerApp::new(config)))),
)
}
struct LogViewerApp {
tabs: Vec<FileTab>,
active_tab_index: usize,
search_panel_state: SearchPanelState,
indexing_state: IndexingState,
search_state: SearchState,
highlight_manager: HighlightManager,
first_frame: bool,
}
impl LogViewerApp {
fn new(config: AppConfig) -> Self {
Self {
tabs: Vec::new(),
active_tab_index: 0,
search_panel_state: SearchPanelState {
query: config.last_search_query,
case_sensitive: config.case_sensitive,
use_regex: config.use_regex,
history: config.search_history,
},
indexing_state: IndexingState::new(),
search_state: SearchState::new(),
highlight_manager: HighlightManager::new(config.highlight_rules),
first_frame: true,
}
}
fn active_tab(&self) -> Option<&FileTab> {
self.tabs.get(self.active_tab_index)
}
fn active_tab_mut(&mut self) -> Option<&mut FileTab> {
self.tabs.get_mut(self.active_tab_index)
}
fn save_config(&self) {
let config = AppConfig {
search_history: self.search_panel_state.history.clone(),
case_sensitive: self.search_panel_state.case_sensitive,
use_regex: self.search_panel_state.use_regex,
last_search_query: self.search_panel_state.query.clone(),
highlight_rules: self.highlight_manager.rules.clone(),
};
config.save();
}
fn handle_open_file(&mut self) {
if let Some(new_tab) = open_file_dialog(&self.indexing_state) {
self.tabs.push(new_tab);
self.active_tab_index = self.tabs.len() - 1;
}
}
fn handle_close_tab(&mut self, index: usize) {
close_tab(&mut self.tabs, &mut self.active_tab_index, index);
}
fn handle_search(&mut self) {
if let Some(tab) = self.active_tab() {
let params = SearchParams {
query: self.search_panel_state.query.clone(),
case_sensitive: self.search_panel_state.case_sensitive,
use_regex: self.search_panel_state.use_regex,
};
let line_index = Arc::clone(&tab.line_index);
let file_path = tab.file_path.clone();
// Clear current results
if let Some(tab) = self.active_tab_mut() {
tab.filtered_lines.clear();
}
start_search(&self.search_state, params, line_index, file_path);
}
}
fn handle_clear_search(&mut self) {
self.search_panel_state.query.clear();
if let Some(tab) = self.active_tab_mut() {
tab.filtered_lines.clear();
}
}
fn handle_keyboard_input(&mut self, ctx: &egui::Context) {
if let Some(tab) = self.active_tab_mut() {
tab.page_scroll_direction = ctx.input(|i| {
if i.key_pressed(egui::Key::PageDown) {
Some(1.0)
} else if i.key_pressed(egui::Key::PageUp) {
Some(-1.0)
} else {
None
}
});
}
}
fn update_search_results(&mut self) {
if let Some(filtered) = self.search_state.take_results() {
if let Some(tab) = self.active_tab_mut() {
tab.filtered_lines = filtered;
tab.filtered_scroll_offset = 0;
}
}
}
}
impl eframe::App for LogViewerApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
if self.first_frame {
self.first_frame = false;
ctx.send_viewport_cmd(egui::ViewportCommand::Maximized(true));
}
// Update search results if available
self.update_search_results();
// Render top menu
let mut open_file_requested = false;
render_top_menu(
ctx,
&mut self.highlight_manager,
&self.indexing_state,
&mut open_file_requested,
);
if open_file_requested {
self.handle_open_file();
}
// Render highlight editor
let highlight_config_changed = render_highlight_editor(ctx, &mut self.highlight_manager);
if highlight_config_changed {
self.save_config();
}
// Render tabs
let mut close_tab_index = None;
render_tabs_panel(
ctx,
&self.tabs,
&mut self.active_tab_index,
&mut close_tab_index,
);
if let Some(index) = close_tab_index {
self.handle_close_tab(index);
}
// Render search panel
let match_count = self.active_tab().map(|t| t.filtered_lines.len()).unwrap_or(0);
let search_actions = render_search_panel(
ctx,
&mut self.search_panel_state,
&self.search_state,
match_count,
);
if search_actions.execute_search {
add_to_history(
&mut self.search_panel_state.history,
&self.search_panel_state.query,
);
self.handle_search();
}
if search_actions.clear_search {
self.handle_clear_search();
}
if search_actions.config_changed {
self.save_config();
}
// Render filtered view
let show_filtered = !self.search_panel_state.query.is_empty();
let highlight_rules = self.highlight_manager.rules.clone();
if show_filtered {
if let Some(tab) = self.active_tab_mut() {
if !tab.filtered_lines.is_empty() {
egui::TopBottomPanel::bottom("filtered_view")
.resizable(true)
.default_height(200.0)
.show(ctx, |ui| {
ui.heading("Filtered View");
ui.separator();
render_log_view(
ui,
LogViewContext {
tab,
highlight_rules: &highlight_rules,
show_all: false,
},
);
});
}
}
}
// Handle keyboard input
self.handle_keyboard_input(ctx);
// Render main view
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading("Main Log View");
ui.separator();
if let Some(tab) = self.active_tab_mut() {
render_log_view(
ui,
LogViewContext {
tab,
highlight_rules: &highlight_rules,
show_all: true,
},
);
} else {
ui.centered_and_justified(|ui| {
ui.label("Click 'Open File' to load a log file");
});
}
});
// Only request repaint if there are ongoing background operations
if self.indexing_state.is_indexing() || self.search_state.is_searching() {
ctx.request_repaint();
}
}
}