more
This commit is contained in:
+25
-16
@@ -1,7 +1,6 @@
|
|||||||
use std::ops::Index;
|
use std::cell::RefCell;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use rfd::MessageDialogResult::No;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use crate::fleen_app::FleenError::{RootDirNonexistenceError, RootDirPopulatedError};
|
use crate::fleen_app::FleenError::{RootDirNonexistenceError, RootDirPopulatedError};
|
||||||
use crate::fleen_app::TreeEntry::{CloseDir, Dir};
|
use crate::fleen_app::TreeEntry::{CloseDir, Dir};
|
||||||
@@ -11,7 +10,9 @@ pub enum FleenError {
|
|||||||
#[error("Can't reach root dir {0}")]
|
#[error("Can't reach root dir {0}")]
|
||||||
RootDirNonexistenceError(PathBuf),
|
RootDirNonexistenceError(PathBuf),
|
||||||
#[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}")]
|
||||||
|
FileOpenError(String, String)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@@ -21,16 +22,15 @@ pub enum TreeEntry {
|
|||||||
CloseDir
|
CloseDir
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct FleenApp {
|
pub struct FleenApp {
|
||||||
root: PathBuf,
|
root: PathBuf,
|
||||||
files_cache: Option<Vec<TreeEntry>>
|
files_cache: RefCell<Option<Vec<TreeEntry>>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FleenApp {
|
impl FleenApp {
|
||||||
pub fn open(root: PathBuf) -> Result<Self, FleenError> {
|
pub fn open(root: PathBuf) -> Result<Self, FleenError> {
|
||||||
match root.try_exists() {
|
match root.try_exists() {
|
||||||
Ok(true) => Ok(Self { root, files_cache: None }),
|
Ok(true) => Ok(Self { root, files_cache: RefCell::new(None) }),
|
||||||
_ => Err(RootDirNonexistenceError(root))
|
_ => Err(RootDirNonexistenceError(root))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -41,7 +41,7 @@ impl FleenApp {
|
|||||||
if iter.next().is_some() {
|
if iter.next().is_some() {
|
||||||
Err(RootDirPopulatedError(root))
|
Err(RootDirPopulatedError(root))
|
||||||
} else {
|
} else {
|
||||||
Ok(Self { root, files_cache: None })
|
Ok(Self { root, files_cache: RefCell::new(None) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
@@ -50,14 +50,15 @@ impl FleenApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn file_tree_entries(&mut self) -> impl IntoIterator<Item=TreeEntry> {
|
// TODO: This is panicky as hell, make it return a Result
|
||||||
if self.files_cache.is_none() {
|
fn refresh_file_cache(&self, force: bool) {
|
||||||
|
if self.files_cache.borrow().is_none() || force {
|
||||||
let mut entries = vec![];
|
let mut entries = vec![];
|
||||||
|
|
||||||
fn visit_dir(dir: &PathBuf, entries: &mut Vec<TreeEntry>) {
|
fn visit_dir(dir: &PathBuf, entries: &mut Vec<TreeEntry>) {
|
||||||
for entry in dir.read_dir().unwrap() {
|
for entry in dir.read_dir().unwrap() {
|
||||||
let path = entry.unwrap().path();
|
let path = entry.unwrap().path();
|
||||||
if path.is_file() {
|
if path.is_file() && !path.file_name().unwrap().to_str().unwrap().starts_with('.') {
|
||||||
entries.push(TreeEntry::File(path))
|
entries.push(TreeEntry::File(path))
|
||||||
} else if path.is_dir() {
|
} else if path.is_dir() {
|
||||||
entries.push(Dir(path.clone()));
|
entries.push(Dir(path.clone()));
|
||||||
@@ -67,15 +68,23 @@ impl FleenApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entries.push(Dir(self.root.clone()));
|
||||||
visit_dir(&self.root, &mut entries);
|
visit_dir(&self.root, &mut entries);
|
||||||
self.files_cache = Some(entries)
|
entries.push(CloseDir);
|
||||||
|
|
||||||
|
self.files_cache.replace(Some(entries));
|
||||||
}
|
}
|
||||||
self.files_cache.as_ref().unwrap().clone()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_file_at_index(&self, index: usize) {
|
pub fn file_tree_entries(&self) -> impl IntoIterator<Item=TreeEntry> {
|
||||||
if let TreeEntry::File(path) = &self.files_cache.as_ref().unwrap()[index] {
|
self.refresh_file_cache(false);
|
||||||
Command::new("open").arg(path).spawn();
|
self.files_cache.borrow().clone().expect("Can't happen because we just refreshed the cache")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn open_filename(&self, filename: &String) -> Result<(), FleenError> {
|
||||||
|
Command::new("open").arg(filename.clone()).spawn().map_err(|err| {
|
||||||
|
FleenError::FileOpenError(filename.clone(), err.to_string())
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+57
-23
@@ -1,30 +1,36 @@
|
|||||||
mod fleen_app;
|
mod fleen_app;
|
||||||
|
|
||||||
use std::fmt::format;
|
use std::path::PathBuf;
|
||||||
use std::path::{Path, PathBuf};
|
use eframe::egui::{Color32, Context, Id, Stroke};
|
||||||
use eframe::egui::{Context, Id};
|
|
||||||
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::{FleenApp, FleenError, TreeEntry};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let native_options = eframe::NativeOptions::default();
|
let native_options = eframe::NativeOptions::default();
|
||||||
eframe::run_native("Fleen", native_options, Box::new(|cc| Ok(Box::new(FleenUi::default()))));
|
eframe::run_native("Fleen", native_options, Box::new(|_cc| {
|
||||||
|
Ok(Box::new(FleenUi::default()))
|
||||||
|
})).expect("Error running application");
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FleenUi {
|
struct FleenUi {
|
||||||
app: Option<FleenApp>,
|
app: Option<FleenApp>,
|
||||||
error: Option<FleenError>
|
error: Option<FleenError>,
|
||||||
|
selected_file: Option<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FleenUi {
|
impl Default for FleenUi {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self { app: None, error: None }
|
Self {
|
||||||
|
app: None,
|
||||||
|
error: None,
|
||||||
|
selected_file: None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FleenUi {
|
impl FleenUi {
|
||||||
fn site_chooser(&mut self, ctx: &Context, frame: &mut Frame) {
|
fn site_chooser(&mut self, ctx: &Context) {
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
ui.label("No site selected!");
|
ui.label("No site selected!");
|
||||||
|
|
||||||
@@ -48,39 +54,63 @@ impl FleenUi {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display(&mut self, ctx: &Context, frame: &mut Frame) {
|
fn display(&mut self, ctx: &Context) {
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
for action in self.tree_view(ui) {
|
ui.vertical(|ui| {
|
||||||
if let Action::Activate(activate) = action {
|
self.tree_view(ui);
|
||||||
for index in activate.selected {
|
if ui.add(egui::Button::new("Open")).clicked() {
|
||||||
self.app.as_ref().unwrap().open_file_at_index(index)
|
if let Some(fname) = &self.selected_file {
|
||||||
|
self.handle_error(self.app.as_ref().unwrap().open_filename(fname))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
ui.add(egui::Button::new("New page")).clicked();
|
||||||
|
});
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tree_view(&mut self, ui: &mut egui::Ui) -> Vec<Action<usize>> {
|
fn tree_view(&mut self, ui: &mut egui::Ui) {
|
||||||
let (_, actions) = egui_ltreeview::TreeView::new(Id::from("tree")).show(ui, |builder| {
|
let tv = egui_ltreeview::TreeView::new(Id::from("tree"))
|
||||||
for (id, entry) in self.app.as_mut().unwrap().file_tree_entries().into_iter().enumerate() {
|
.allow_multi_selection(false)
|
||||||
|
.allow_drag_and_drop(false);
|
||||||
|
let (_, actions) = tv.show(ui, |builder| {
|
||||||
|
for entry in self.app.as_mut().unwrap().file_tree_entries().into_iter() {
|
||||||
match entry {
|
match entry {
|
||||||
TreeEntry::File(p) => builder.leaf(id, label_for_path(p)),
|
TreeEntry::File(p) => builder.leaf(id_for_path(&p), label_for_path(&p)),
|
||||||
TreeEntry::Dir(p) => { builder.dir(id, label_for_path(p)); },
|
TreeEntry::Dir(p) => { builder.dir(id_for_path(&p), label_for_path(&p)); },
|
||||||
TreeEntry::CloseDir => builder.close_dir()
|
TreeEntry::CloseDir => builder.close_dir()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
actions
|
|
||||||
|
for action in actions {
|
||||||
|
match action {
|
||||||
|
Action::SetSelected(files) => {
|
||||||
|
self.selected_file = files.first().map(String::clone)
|
||||||
|
}
|
||||||
|
Action::Activate(activate) => {
|
||||||
|
for fname in activate.selected {
|
||||||
|
self.handle_error(self.app.as_ref().unwrap().open_filename(&fname))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_error(&mut self, result: Result<(), FleenError>) {
|
||||||
|
if let Err(e) = result {
|
||||||
|
self.error = Some(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl eframe::App for FleenUi {
|
impl eframe::App for FleenUi {
|
||||||
fn update(&mut self, ctx: &Context, frame: &mut Frame) {
|
fn update(&mut self, ctx: &Context, _frame: &mut Frame) {
|
||||||
match &mut self.app {
|
match &mut self.app {
|
||||||
None => self.site_chooser(ctx, frame),
|
None => self.site_chooser(ctx),
|
||||||
Some(app) => self.display(ctx, frame)
|
Some(_) => self.display(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(err) = &self.error {
|
if let Some(err) = &self.error {
|
||||||
@@ -95,6 +125,10 @@ impl eframe::App for FleenUi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn label_for_path(path: PathBuf) -> String {
|
fn label_for_path(path: &PathBuf) -> String {
|
||||||
path.file_name().unwrap().to_string_lossy().to_string()
|
path.file_name().unwrap().to_string_lossy().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn id_for_path(path: &PathBuf) -> String {
|
||||||
|
path.to_string_lossy().to_string()
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user