cleaner UI
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -2669,7 +2669,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rlogg"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"eframe",
|
||||
"rayon",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rlogg"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
edition = "2024"
|
||||
authors = ["Stanislav Pastushenko <staspast1@gmail.com>"]
|
||||
description = "A fast log file viewer with search, filtering, and highlighting capabilities"
|
||||
|
||||
104
src/main.rs
104
src/main.rs
@@ -7,6 +7,7 @@ mod highlight;
|
||||
mod line_index;
|
||||
mod search;
|
||||
mod tab_manager;
|
||||
mod theme;
|
||||
mod types;
|
||||
mod ui;
|
||||
|
||||
@@ -38,7 +39,11 @@ fn main() -> eframe::Result {
|
||||
eframe::run_native(
|
||||
"RLogg",
|
||||
options,
|
||||
Box::new(move |_cc| Ok(Box::new(LogViewerApp::new(config)))),
|
||||
Box::new(move |cc| {
|
||||
// Apply the modern dark purple theme
|
||||
theme::apply_theme(&cc.egui_ctx);
|
||||
Ok(Box::new(LogViewerApp::new(config)))
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -219,18 +224,44 @@ impl eframe::App for LogViewerApp {
|
||||
self.save_config();
|
||||
}
|
||||
|
||||
// Render filtered view
|
||||
// Render filtered view with improved styling
|
||||
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() {
|
||||
let palette = theme::get_palette();
|
||||
|
||||
egui::TopBottomPanel::bottom("filtered_view")
|
||||
.resizable(true)
|
||||
.default_height(200.0)
|
||||
.default_height(250.0)
|
||||
.min_height(150.0)
|
||||
.show(ctx, |ui| {
|
||||
ui.heading("Filtered View");
|
||||
// Styled header
|
||||
let header_frame = egui::Frame::none()
|
||||
.fill(palette.bg_secondary)
|
||||
.inner_margin(egui::Margin::symmetric(12.0, 8.0));
|
||||
|
||||
header_frame.show(ui, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
let icon = egui::RichText::new("🔍")
|
||||
.size(16.0);
|
||||
ui.label(icon);
|
||||
|
||||
let title = egui::RichText::new("Search Results")
|
||||
.size(16.0)
|
||||
.color(palette.text_primary)
|
||||
.strong();
|
||||
ui.label(title);
|
||||
|
||||
let count = egui::RichText::new(format!("({} matches)", tab.filtered_lines.len()))
|
||||
.size(14.0)
|
||||
.color(palette.accent_bright);
|
||||
ui.label(count);
|
||||
});
|
||||
});
|
||||
|
||||
ui.separator();
|
||||
|
||||
render_log_view(
|
||||
@@ -249,12 +280,49 @@ impl eframe::App for LogViewerApp {
|
||||
// Handle keyboard input
|
||||
self.handle_keyboard_input(ctx);
|
||||
|
||||
// Render main view
|
||||
// Render main view with improved styling
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.heading("Main Log View");
|
||||
ui.separator();
|
||||
let palette = theme::get_palette();
|
||||
|
||||
if let Some(tab) = self.active_tab_mut() {
|
||||
// Styled header
|
||||
let header_frame = egui::Frame::none()
|
||||
.fill(palette.bg_secondary)
|
||||
.inner_margin(egui::Margin::symmetric(12.0, 8.0));
|
||||
|
||||
header_frame.show(ui, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
let icon = egui::RichText::new("📄")
|
||||
.size(16.0);
|
||||
ui.label(icon);
|
||||
|
||||
let title = egui::RichText::new("Log View")
|
||||
.size(16.0)
|
||||
.color(palette.text_primary)
|
||||
.strong();
|
||||
ui.label(title);
|
||||
|
||||
// Show filename
|
||||
if let Some(filename) = tab.file_path.file_name() {
|
||||
ui.add_space(8.0);
|
||||
let filename_text = egui::RichText::new(format!("• {}", filename.to_string_lossy()))
|
||||
.size(14.0)
|
||||
.color(palette.text_secondary);
|
||||
ui.label(filename_text);
|
||||
}
|
||||
|
||||
// Show line count
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||
let line_count = egui::RichText::new(format!("{} lines", tab.line_index.total_lines))
|
||||
.size(13.0)
|
||||
.color(palette.text_muted);
|
||||
ui.label(line_count);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
ui.separator();
|
||||
|
||||
render_log_view(
|
||||
ui,
|
||||
LogViewContext {
|
||||
@@ -265,7 +333,27 @@ impl eframe::App for LogViewerApp {
|
||||
);
|
||||
} else {
|
||||
ui.centered_and_justified(|ui| {
|
||||
ui.label("Click 'Open File' to load a log file");
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add_space(40.0);
|
||||
|
||||
let icon = egui::RichText::new("📂")
|
||||
.size(48.0);
|
||||
ui.label(icon);
|
||||
|
||||
ui.add_space(16.0);
|
||||
|
||||
let text = egui::RichText::new("No file loaded")
|
||||
.size(18.0)
|
||||
.color(palette.text_secondary);
|
||||
ui.label(text);
|
||||
|
||||
ui.add_space(8.0);
|
||||
|
||||
let hint = egui::RichText::new("Click 'Open File' in the menu to get started")
|
||||
.size(14.0)
|
||||
.color(palette.text_muted);
|
||||
ui.label(hint);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use eframe::egui;
|
||||
|
||||
use crate::file_tab::FileTab;
|
||||
use crate::theme;
|
||||
use crate::types::HighlightRule;
|
||||
|
||||
pub struct LogViewContext<'a> {
|
||||
@@ -86,7 +87,7 @@ fn handle_scroll_to_line(
|
||||
) {
|
||||
if tab.scroll_to_main {
|
||||
let target_row = tab.main_scroll_offset;
|
||||
let adgusted_row_height = row_height + 3f32;
|
||||
let adgusted_row_height = row_height + 6f32;
|
||||
let scroll_offset = (target_row as f32) * adgusted_row_height;
|
||||
|
||||
eprintln!("=== SCROLL TO LINE ===");
|
||||
@@ -110,7 +111,7 @@ fn handle_page_scroll(
|
||||
total_lines: usize,
|
||||
) {
|
||||
if let Some(direction) = tab.page_scroll_direction.take() {
|
||||
let row_height_offset = row_height + 3f32;
|
||||
let row_height_offset = row_height + 6f32;
|
||||
let viewport_height = ui.available_height();
|
||||
let rows_per_page = (viewport_height / row_height_offset).floor().max(1.0);
|
||||
let scroll_delta = direction * rows_per_page * row_height_offset;
|
||||
@@ -192,49 +193,59 @@ fn render_line<F>(
|
||||
) where
|
||||
F: FnOnce(bool),
|
||||
{
|
||||
let palette = theme::get_palette();
|
||||
|
||||
let highlight_color = highlight_rules
|
||||
.iter()
|
||||
.find(|rule| rule.enabled && content.contains(&rule.pattern))
|
||||
.map(|rule| egui::Color32::from_rgb(rule.color[0], rule.color[1], rule.color[2]));
|
||||
|
||||
// Improved color scheme with better visual hierarchy
|
||||
let bg_color = if is_selected {
|
||||
egui::Color32::from_rgb(70, 130, 180)
|
||||
palette.selection
|
||||
} else if let Some(color) = highlight_color {
|
||||
color
|
||||
} else {
|
||||
egui::Color32::TRANSPARENT
|
||||
};
|
||||
|
||||
// Better padding and margins for improved readability
|
||||
let frame = egui::Frame::none()
|
||||
.fill(bg_color)
|
||||
.inner_margin(egui::Margin::symmetric(2.0, 1.0));
|
||||
.inner_margin(egui::Margin::symmetric(8.0, 3.0));
|
||||
|
||||
frame.show(ui, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
let line_num_text = egui::RichText::new(format!("{:6} ", line_num + 1))
|
||||
// Line numbers with better styling
|
||||
let line_num_text = egui::RichText::new(format!("{:6}", line_num + 1))
|
||||
.monospace()
|
||||
.color(if is_selected {
|
||||
egui::Color32::WHITE
|
||||
palette.text_primary
|
||||
} else {
|
||||
egui::Color32::DARK_GRAY
|
||||
palette.line_number
|
||||
});
|
||||
|
||||
let line_num_response = ui.label(line_num_text);
|
||||
|
||||
// Separator between line number and content
|
||||
ui.add_space(4.0);
|
||||
ui.separator();
|
||||
ui.add_space(4.0);
|
||||
|
||||
// Content text with improved styling
|
||||
let text = egui::RichText::new(content).monospace().color(
|
||||
if is_selected {
|
||||
egui::Color32::WHITE
|
||||
palette.text_primary
|
||||
} else {
|
||||
ui.style().visuals.text_color()
|
||||
palette.text_primary
|
||||
},
|
||||
);
|
||||
|
||||
let text_response = ui
|
||||
.scope(|ui| {
|
||||
ui.style_mut().visuals.selection.bg_fill =
|
||||
egui::Color32::from_rgb(255, 180, 50);
|
||||
ui.style_mut().visuals.selection.stroke.color =
|
||||
egui::Color32::from_rgb(200, 140, 30);
|
||||
// Better text selection colors
|
||||
ui.style_mut().visuals.selection.bg_fill = palette.accent_bright;
|
||||
ui.style_mut().visuals.selection.stroke.color = palette.accent_primary;
|
||||
ui.add(egui::Label::new(text).selectable(true))
|
||||
})
|
||||
.inner;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use eframe::egui;
|
||||
|
||||
use crate::search::SearchState;
|
||||
use crate::theme;
|
||||
|
||||
pub struct SearchPanelState {
|
||||
pub query: String,
|
||||
@@ -27,15 +28,29 @@ pub fn render_search_panel(
|
||||
config_changed: false,
|
||||
};
|
||||
|
||||
egui::TopBottomPanel::bottom("search_panel").show(ctx, |ui| {
|
||||
let palette = theme::get_palette();
|
||||
|
||||
egui::TopBottomPanel::bottom("search_panel")
|
||||
.frame(egui::Frame::none()
|
||||
.fill(palette.bg_secondary)
|
||||
.inner_margin(egui::Margin::symmetric(12.0, 10.0)))
|
||||
.show(ctx, |ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("🔍 Filter:");
|
||||
// Filter label with icon
|
||||
let label = egui::RichText::new("🔍 Filter:")
|
||||
.size(14.0)
|
||||
.color(palette.text_primary);
|
||||
ui.label(label);
|
||||
|
||||
let text_edit_width = 200.0;
|
||||
let text_response = ui.add_sized(
|
||||
[text_edit_width, 20.0],
|
||||
egui::TextEdit::singleline(&mut state.query),
|
||||
ui.add_space(4.0);
|
||||
|
||||
// Text input with proper height matching buttons
|
||||
let text_edit_width = 300.0;
|
||||
let text_response = ui.add(
|
||||
egui::TextEdit::singleline(&mut state.query)
|
||||
.desired_width(text_edit_width)
|
||||
.hint_text("Enter search query...")
|
||||
);
|
||||
|
||||
let enter_pressed =
|
||||
@@ -45,6 +60,9 @@ pub fn render_search_panel(
|
||||
render_history_dropdown(ui, state);
|
||||
}
|
||||
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Checkboxes
|
||||
let case_changed = ui
|
||||
.checkbox(&mut state.case_sensitive, "Case sensitive")
|
||||
.changed();
|
||||
@@ -54,21 +72,32 @@ pub fn render_search_panel(
|
||||
actions.config_changed = true;
|
||||
}
|
||||
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Search button
|
||||
if ui.button("Search").clicked() || enter_pressed {
|
||||
actions.execute_search = true;
|
||||
actions.config_changed = true;
|
||||
}
|
||||
|
||||
// Clear button and match count
|
||||
if !state.query.is_empty() {
|
||||
if ui.button("✖ Clear").clicked() {
|
||||
actions.clear_search = true;
|
||||
}
|
||||
|
||||
ui.label(format!("({} matches)", match_count));
|
||||
ui.add_space(8.0);
|
||||
|
||||
let count_text = egui::RichText::new(format!("{} matches", match_count))
|
||||
.color(palette.accent_bright)
|
||||
.size(13.0);
|
||||
ui.label(count_text);
|
||||
}
|
||||
});
|
||||
|
||||
// Progress bar
|
||||
if search_state.is_searching() {
|
||||
ui.add_space(6.0);
|
||||
let progress = search_state.get_progress();
|
||||
ui.horizontal(|ui| {
|
||||
ui.add(
|
||||
|
||||
Reference in New Issue
Block a user