Add folder tree

This commit is contained in:
2025-06-19 23:09:41 +02:00
parent 4e9eacc7b0
commit b39419888b
5 changed files with 291 additions and 17 deletions

View File

@ -6,7 +6,7 @@ use std::{
time::{Duration, Instant},
};
use crate::{file_editor::FileEditor, preferences::Preferences, util::GuiSender};
use crate::{file_editor::FileEditor, folder::Folder, preferences::Preferences, util::GuiSender};
use egui::{
Align, Button, Color32, Context, FontData, FontDefinitions, Key, Modifiers, PointerButton,
RichText, ScrollArea, Stroke,
@ -25,6 +25,9 @@ pub struct App {
jobs: Jobs,
tabs: Vec<(TabId, Tab)>,
folders: Vec<Folder>,
open_tab_index: Option<usize>,
next_tab_id: TabId,
@ -80,6 +83,7 @@ pub type TabId = usize;
pub enum Action {
OpenFile(FileEditor),
OpenFolder(Folder),
MoveFile(TabId, PathBuf),
CloseTab(TabId),
// TODO
@ -102,6 +106,7 @@ impl Default for App {
tabs: vec![(1, Tab::File(FileEditor::new("note.md")))],
open_tab_index: None,
next_tab_id: 2,
folders: vec![],
}
}
}
@ -194,6 +199,18 @@ impl App {
fn handle_action(&mut self, action: Action) {
match action {
Action::OpenFolder(new_folder) => {
if let Some(folder) = self
.folders
.iter_mut()
.find(|folder| folder.path() == new_folder.path())
{
*folder = new_folder;
} else {
self.folders.push(new_folder);
self.folders.sort_by(|a, b| a.name().cmp(b.name()));
}
}
Action::OpenFile(file_editor) => {
self.open_tab(Tab::File(file_editor));
}
@ -262,7 +279,12 @@ impl eframe::App for App {
}
if ui.button("Open Folder").clicked() {
log::error!("Open Folder not implemented");
self.jobs.start(ui.ctx(), move || {
let path = rfd::FileDialog::new().pick_folder()?;
let name = path.file_name()?.to_string_lossy().to_string();
let folder = Folder::NotLoaded { name, path };
Some(Action::OpenFolder(folder))
});
}
if ui
@ -277,8 +299,8 @@ impl eframe::App for App {
let can_save_file = self
.open_tab_index
.and_then(|i| self.tabs.get(i))
.and_then(|(id, tab)| match tab {
Tab::File(file_editor) => Some((*id, file_editor)),
.map(|(id, tab)| match tab {
Tab::File(file_editor) => (*id, file_editor),
})
.and_then(|(_, file_editor)| file_editor.path().zip(Some(file_editor)))
.is_some();
@ -287,12 +309,11 @@ impl eframe::App for App {
self.save_active_tab(ui.ctx());
}
let open_file =
self.open_tab_index
.and_then(|i| self.tabs.get(i))
.and_then(|(id, tab)| match tab {
Tab::File(file_editor) => Some((*id, file_editor)),
});
let open_file = self.open_tab_index.and_then(|i| self.tabs.get(i)).map(
|(id, tab)| match tab {
Tab::File(file_editor) => (*id, file_editor),
},
);
#[cfg(not(target_arch = "wasm32"))]
if ui
@ -351,6 +372,39 @@ impl eframe::App for App {
});
});
egui::SidePanel::left("file browser")
.resizable(true)
.show(ctx, |ui| {
if ui.button("refresh").clicked() {
for folder in &mut self.folders {
folder.unload();
}
}
ScrollArea::both().auto_shrink(false).show(ui, |ui| {
self.folders.retain_mut(|folder| {
let response = folder.show(ui);
if let Some(file_path) = response.open_file {
let file_path = file_path.to_owned();
self.jobs.start(ui.ctx(), move || {
let text = fs::read_to_string(&file_path)
.inspect_err(|e| {
log::error!("Failed to read {file_path:?}: {e}")
})
.ok()?;
let editor = FileEditor::from_file(file_path, &text);
Some(Action::OpenFile(editor))
});
}
// delete on right-click
!response.clicked_by(PointerButton::Secondary)
});
});
});
egui::CentralPanel::default().show(ctx, |ui| {
if let Some(Tab::File(file_editor)) = self
.open_tab_index
@ -389,8 +443,8 @@ impl App {
let open_file = self
.open_tab_index
.and_then(|i| self.tabs.get_mut(i))
.and_then(|(id, tab)| match tab {
Tab::File(file_editor) => Some((*id, file_editor)),
.map(|(id, tab)| match tab {
Tab::File(file_editor) => (*id, file_editor),
})
.and_then(|(_, file_editor)| {
file_editor