deletion
This commit is contained in:
+46
-2
@@ -1,4 +1,6 @@
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::fs;
|
||||||
|
use std::io::Error;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
@@ -12,7 +14,11 @@ pub enum FleenError {
|
|||||||
#[error("Root dir is nonempty, you probably don't want to create an app here: {0}")]
|
#[error("Root dir is nonempty, you probably don't want to create an app here: {0}")]
|
||||||
RootDirPopulatedError(PathBuf),
|
RootDirPopulatedError(PathBuf),
|
||||||
#[error("Failed to open {0}: {1}")]
|
#[error("Failed to open {0}: {1}")]
|
||||||
FileOpenError(String, String)
|
FileIoError(String, String),
|
||||||
|
#[error("Can't create {0} because it already exists")]
|
||||||
|
FileExistsError(PathBuf),
|
||||||
|
#[error("Can't create {0}: {1}")]
|
||||||
|
FileCreateError(PathBuf, String)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@@ -22,6 +28,11 @@ pub enum TreeEntry {
|
|||||||
CloseDir
|
CloseDir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
|
pub enum FileType {
|
||||||
|
File, Dir
|
||||||
|
}
|
||||||
|
|
||||||
pub struct FleenApp {
|
pub struct FleenApp {
|
||||||
root: PathBuf,
|
root: PathBuf,
|
||||||
files_cache: RefCell<Option<Vec<TreeEntry>>>
|
files_cache: RefCell<Option<Vec<TreeEntry>>>
|
||||||
@@ -83,8 +94,41 @@ impl FleenApp {
|
|||||||
|
|
||||||
pub fn open_filename(&self, filename: &String) -> Result<(), FleenError> {
|
pub fn open_filename(&self, filename: &String) -> Result<(), FleenError> {
|
||||||
Command::new("open").arg(filename.clone()).spawn().map_err(|err| {
|
Command::new("open").arg(filename.clone()).spawn().map_err(|err| {
|
||||||
FleenError::FileOpenError(filename.clone(), err.to_string())
|
FleenError::FileIoError(filename.clone(), err.to_string())
|
||||||
})?;
|
})?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_page(&self, file_type: FileType, name: &String, parent: Option<&String>) -> Result<(), FleenError> {
|
||||||
|
let mut target = match parent {
|
||||||
|
Some(s) => PathBuf::from(s),
|
||||||
|
None => self.root.clone()
|
||||||
|
};
|
||||||
|
target.push(name.clone());
|
||||||
|
if target.exists() {
|
||||||
|
return Err(FleenError::FileExistsError(target))
|
||||||
|
}
|
||||||
|
|
||||||
|
match file_type {
|
||||||
|
FileType::File => std::fs::write(target.clone(), []),
|
||||||
|
FileType::Dir => std::fs::create_dir(target.clone())
|
||||||
|
}.map_err(|err| FleenError::FileCreateError(target.clone(), err.to_string()))?;
|
||||||
|
|
||||||
|
self.refresh_file_cache(true);
|
||||||
|
if file_type == FileType::File {
|
||||||
|
self.open_filename(&target.to_string_lossy().to_string())?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete_page(&self, path: &String) -> Result<(), FleenError> {
|
||||||
|
let target = PathBuf::from(path);
|
||||||
|
if target.is_dir() {
|
||||||
|
fs::remove_dir_all(target)
|
||||||
|
} else {
|
||||||
|
fs::remove_file(target)
|
||||||
|
}.map_err(|err| FleenError::FileIoError(path.clone(), err.to_string()))?;
|
||||||
|
self.refresh_file_cache(true);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+101
-4
@@ -4,7 +4,7 @@ use std::path::PathBuf;
|
|||||||
use eframe::egui::{Color32, Context, Id, Stroke};
|
use eframe::egui::{Color32, Context, Id, Stroke};
|
||||||
use eframe::{egui, Frame};
|
use eframe::{egui, Frame};
|
||||||
use egui_ltreeview::Action;
|
use egui_ltreeview::Action;
|
||||||
use crate::fleen_app::{FleenApp, FleenError, TreeEntry};
|
use crate::fleen_app::{FileType, FleenApp, FleenError, TreeEntry};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let native_options = eframe::NativeOptions::default();
|
let native_options = eframe::NativeOptions::default();
|
||||||
@@ -13,10 +13,17 @@ fn main() {
|
|||||||
})).expect("Error running application");
|
})).expect("Error running application");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum DialogMode {
|
||||||
|
NewFile(String),
|
||||||
|
ConfirmDelete(String),
|
||||||
|
RenameFile(String)
|
||||||
|
}
|
||||||
|
|
||||||
struct FleenUi {
|
struct FleenUi {
|
||||||
app: Option<FleenApp>,
|
app: Option<FleenApp>,
|
||||||
error: Option<FleenError>,
|
error: Option<FleenError>,
|
||||||
selected_file: Option<String>
|
selected_file: Option<String>,
|
||||||
|
dialog_mode: Option<DialogMode>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FleenUi {
|
impl Default for FleenUi {
|
||||||
@@ -24,7 +31,8 @@ impl Default for FleenUi {
|
|||||||
Self {
|
Self {
|
||||||
app: None,
|
app: None,
|
||||||
error: None,
|
error: None,
|
||||||
selected_file: None
|
selected_file: None,
|
||||||
|
dialog_mode: None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,6 +63,8 @@ impl FleenUi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn display(&mut self, ctx: &Context) {
|
fn display(&mut self, ctx: &Context) {
|
||||||
|
let mut new_clicked = false;
|
||||||
|
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
@@ -64,10 +74,31 @@ impl FleenUi {
|
|||||||
self.handle_error(self.app.as_ref().unwrap().open_filename(fname))
|
self.handle_error(self.app.as_ref().unwrap().open_filename(fname))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ui.add(egui::Button::new("New page")).clicked();
|
|
||||||
|
if ui.add(egui::Button::new("New page")).clicked() {
|
||||||
|
new_clicked = true;
|
||||||
|
self.dialog_mode = Some(DialogMode::NewFile(String::new()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let delete_btn = egui::Button::new("Delete").fill(Color32::DARK_RED);
|
||||||
|
if let Some(selected) = &self.selected_file {
|
||||||
|
if ui.add(delete_btn).clicked() {
|
||||||
|
self.dialog_mode = Some(DialogMode::ConfirmDelete(selected.clone()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ui.add_enabled(false, delete_btn);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
match self.dialog_mode {
|
||||||
|
Some(DialogMode::NewFile(_)) => self.new_file_dialog(ctx, new_clicked),
|
||||||
|
Some(DialogMode::ConfirmDelete(_)) => self.confirm_delete_dialog(ctx),
|
||||||
|
Some(DialogMode::RenameFile(_)) => todo!(),
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tree_view(&mut self, ui: &mut egui::Ui) {
|
fn tree_view(&mut self, ui: &mut egui::Ui) {
|
||||||
@@ -99,6 +130,72 @@ impl FleenUi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn new_file_dialog(&mut self, ctx: &Context, just_clicked: bool) {
|
||||||
|
egui::Window::new("New page").collapsible(false).resizable(false).show(ctx, |ui| {
|
||||||
|
ui.label("Name");
|
||||||
|
// If we press enter on the text field, it emits lost_focus. We can't just create the file based
|
||||||
|
// on that, because it's not the only thing that causes it, though. So instead we'll just focus the
|
||||||
|
// create file button
|
||||||
|
//let fname = match &mut self.dialog_mode { Some(DialogMode::NewFile(s)) => s, _ => unreachable!() };
|
||||||
|
let Some(DialogMode::NewFile(fname)) = &mut self.dialog_mode else { unreachable!() };
|
||||||
|
let name_field = egui::TextEdit::singleline(fname);
|
||||||
|
let resp = ui.add(name_field);
|
||||||
|
let enter_key = resp.lost_focus();
|
||||||
|
|
||||||
|
// We want to focus the text field if we just opened the dialog, but egui doesn't currently
|
||||||
|
// offer a way to do that. However, we can pass in a bool for if the button that opens the
|
||||||
|
// dialog was just clicked, and use that to tell whether we should grab focus. It's only true
|
||||||
|
// the exact frame that the dialog was opened, which is what we're looking for.
|
||||||
|
if just_clicked { resp.request_focus() }
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
let mut make_thing = |file_type: FileType| {
|
||||||
|
let Some(DialogMode::NewFile(fname)) = &self.dialog_mode else { unreachable!() };
|
||||||
|
let app = self.app.as_ref().unwrap();
|
||||||
|
let r = app.create_page(file_type,
|
||||||
|
fname,
|
||||||
|
self.selected_file.as_ref());
|
||||||
|
if r.is_err() {
|
||||||
|
self.handle_error(r);
|
||||||
|
} else {
|
||||||
|
self.dialog_mode = None; // Close the dialog, we're done
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let btn = ui.button("New file");
|
||||||
|
if enter_key { btn.request_focus() }
|
||||||
|
if btn.clicked() { make_thing(FileType::File) }
|
||||||
|
if ui.button("New directory").clicked() { make_thing(FileType::Dir) }
|
||||||
|
if ui.button("Cancel").clicked() {
|
||||||
|
self.dialog_mode = None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn confirm_delete_dialog(&mut self, ctx: &Context) {
|
||||||
|
let (mut del, mut cancel) = (false, false);
|
||||||
|
let Some(DialogMode::ConfirmDelete(fname)) = &self.dialog_mode else { unreachable!() };
|
||||||
|
egui::Window::new("Are you sure?").collapsible(false).resizable(false).show(ctx, |ui| {
|
||||||
|
let file = label_for_path(&PathBuf::from(fname));
|
||||||
|
ui.heading(format!("Really delete {}?", file));
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
let btn = egui::Button::new("Yep, I'm sure").fill(Color32::DARK_RED);
|
||||||
|
del = ui.add(btn).clicked();
|
||||||
|
cancel = ui.button("Cancel").clicked();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if del {
|
||||||
|
let r = self.app.as_ref().unwrap().delete_page(&fname.clone());
|
||||||
|
self.dialog_mode = None;
|
||||||
|
if r.is_err() { self.handle_error(r); }
|
||||||
|
self.selected_file = None;
|
||||||
|
} else if cancel {
|
||||||
|
self.dialog_mode = None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_error(&mut self, result: Result<(), FleenError>) {
|
fn handle_error(&mut self, result: Result<(), FleenError>) {
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
self.error = Some(e)
|
self.error = Some(e)
|
||||||
|
|||||||
Reference in New Issue
Block a user