use std::path::PathBuf;
use serde::{Deserialize, Serialize};
#[derive(Clone, Default, Serialize, Deserialize)]
pub struct RecentFiles {
paths: Vec<PathBuf>,
max_entries: usize,
}
pub enum RecentFilesAction {
Open(PathBuf),
Clear,
}
impl RecentFiles {
pub fn new(max_entries: usize) -> Self {
Self {
paths: Vec::new(),
max_entries,
}
}
pub fn add(&mut self, path: PathBuf) {
self.paths.retain(|existing| existing != &path);
self.paths.insert(0, path);
self.paths.truncate(self.max_entries);
}
pub fn remove(&mut self, path: &PathBuf) {
self.paths.retain(|existing| existing != path);
}
pub fn clear(&mut self) {
self.paths.clear();
}
pub fn paths(&self) -> &[PathBuf] {
&self.paths
}
pub fn is_empty(&self) -> bool {
self.paths.is_empty()
}
pub fn len(&self) -> usize {
self.paths.len()
}
pub fn render_menu(
&self,
ui: &mut egui::Ui,
pinned_path: Option<&str>,
display_name_fn: impl Fn(&std::path::Path) -> String,
) -> Option<RecentFilesAction> {
let mut action = None;
ui.label(
egui::RichText::new("Recent Projects")
.color(egui::Color32::from_rgb(150, 150, 150))
.small()
.strong(),
);
ui.add_space(4.0);
if self.paths.is_empty() {
ui.label(
egui::RichText::new(" No recent projects")
.color(egui::Color32::from_rgb(120, 120, 120))
.small()
.italics(),
);
} else {
for path in &self.paths {
let path_string = path.to_string_lossy().to_string();
let project_name = display_name_fn(path);
let is_pinned = pinned_path.is_some_and(|pinned| path_string == pinned);
let display_name = if project_name.trim().is_empty() {
if is_pinned {
"⭐ Untitled".to_string()
} else {
"Untitled".to_string()
}
} else if is_pinned {
format!("⭐ {}", project_name)
} else {
project_name
};
let button = ui
.add(
egui::Button::new(
egui::RichText::new(display_name)
.color(egui::Color32::from_rgb(100, 200, 255)),
)
.min_size(egui::vec2(200.0, 0.0)),
)
.on_hover_text(&path_string);
if button.clicked() {
action = Some(RecentFilesAction::Open(path.clone()));
ui.close();
}
}
ui.add_space(4.0);
if ui
.button(
egui::RichText::new("Clear Recent Projects")
.color(egui::Color32::from_rgb(255, 100, 100))
.small(),
)
.clicked()
{
action = Some(RecentFilesAction::Clear);
ui.close();
}
}
action
}
}