initial commit, enjoy the ride
This commit is contained in:
@@ -0,0 +1 @@
|
||||
/target
|
||||
Generated
+8
@@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
Generated
+11
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="EMPTY_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
Generated
+8
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/fleen.iml" filepath="$PROJECT_DIR$/.idea/fleen.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+4187
File diff suppressed because it is too large
Load Diff
+10
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "fleen"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
eframe = "0.32.1"
|
||||
egui_ltreeview = "0.5.3"
|
||||
rfd = "0.15.4"
|
||||
thiserror = "2.0.16"
|
||||
@@ -0,0 +1,81 @@
|
||||
use std::ops::Index;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use rfd::MessageDialogResult::No;
|
||||
use thiserror::Error;
|
||||
use crate::fleen_app::FleenError::{RootDirNonexistenceError, RootDirPopulatedError};
|
||||
use crate::fleen_app::TreeEntry::{CloseDir, Dir};
|
||||
|
||||
#[derive(Error, Debug, Clone)]
|
||||
pub enum FleenError {
|
||||
#[error("Can't reach root dir {0}")]
|
||||
RootDirNonexistenceError(PathBuf),
|
||||
#[error("Root dir is nonempty, you probably don't want to create an app here: {0}")]
|
||||
RootDirPopulatedError(PathBuf)
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum TreeEntry {
|
||||
File(PathBuf),
|
||||
Dir(PathBuf),
|
||||
CloseDir
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FleenApp {
|
||||
root: PathBuf,
|
||||
files_cache: Option<Vec<TreeEntry>>
|
||||
}
|
||||
|
||||
impl FleenApp {
|
||||
pub fn open(root: PathBuf) -> Result<Self, FleenError> {
|
||||
match root.try_exists() {
|
||||
Ok(true) => Ok(Self { root, files_cache: None }),
|
||||
_ => Err(RootDirNonexistenceError(root))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create(root: PathBuf) -> Result<Self, FleenError> {
|
||||
match root.read_dir() {
|
||||
Ok(mut iter) => {
|
||||
if iter.next().is_some() {
|
||||
Err(RootDirPopulatedError(root))
|
||||
} else {
|
||||
Ok(Self { root, files_cache: None })
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
Err(RootDirNonexistenceError(root))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file_tree_entries(&mut self) -> impl IntoIterator<Item=TreeEntry> {
|
||||
if self.files_cache.is_none() {
|
||||
let mut entries = vec![];
|
||||
|
||||
fn visit_dir(dir: &PathBuf, entries: &mut Vec<TreeEntry>) {
|
||||
for entry in dir.read_dir().unwrap() {
|
||||
let path = entry.unwrap().path();
|
||||
if path.is_file() {
|
||||
entries.push(TreeEntry::File(path))
|
||||
} else if path.is_dir() {
|
||||
entries.push(Dir(path.clone()));
|
||||
visit_dir(&path, entries);
|
||||
entries.push(CloseDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
visit_dir(&self.root, &mut entries);
|
||||
self.files_cache = Some(entries)
|
||||
}
|
||||
self.files_cache.as_ref().unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn open_file_at_index(&self, index: usize) {
|
||||
if let TreeEntry::File(path) = &self.files_cache.as_ref().unwrap()[index] {
|
||||
Command::new("open").arg(path).spawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
mod fleen_app;
|
||||
|
||||
use std::fmt::format;
|
||||
use std::path::{Path, PathBuf};
|
||||
use eframe::egui::{Context, Id};
|
||||
use eframe::{egui, Frame};
|
||||
use egui_ltreeview::Action;
|
||||
use crate::fleen_app::{FleenApp, FleenError, TreeEntry};
|
||||
|
||||
fn main() {
|
||||
let native_options = eframe::NativeOptions::default();
|
||||
eframe::run_native("Fleen", native_options, Box::new(|cc| Ok(Box::new(FleenUi::default()))));
|
||||
}
|
||||
|
||||
struct FleenUi {
|
||||
app: Option<FleenApp>,
|
||||
error: Option<FleenError>
|
||||
}
|
||||
|
||||
impl Default for FleenUi {
|
||||
fn default() -> Self {
|
||||
Self { app: None, error: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl FleenUi {
|
||||
fn site_chooser(&mut self, ctx: &Context, frame: &mut Frame) {
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.label("No site selected!");
|
||||
|
||||
if ui.button("Open site...").clicked() {
|
||||
if let Some(path) = rfd::FileDialog::new().pick_folder() {
|
||||
match FleenApp::open(path) {
|
||||
Ok(app) => { self.app = Some(app) }
|
||||
Err(err) => { self.error = Some(err) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ui.button("New site...").clicked() {
|
||||
if let Some(path) = rfd::FileDialog::new().pick_folder() {
|
||||
match FleenApp::create(path) {
|
||||
Ok(app) => { self.app = Some(app) }
|
||||
Err(err) => { self.error = Some(err) }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn display(&mut self, ctx: &Context, frame: &mut Frame) {
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
for action in self.tree_view(ui) {
|
||||
if let Action::Activate(activate) = action {
|
||||
for index in activate.selected {
|
||||
self.app.as_ref().unwrap().open_file_at_index(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn tree_view(&mut self, ui: &mut egui::Ui) -> Vec<Action<usize>> {
|
||||
let (_, actions) = egui_ltreeview::TreeView::new(Id::from("tree")).show(ui, |builder| {
|
||||
for (id, entry) in self.app.as_mut().unwrap().file_tree_entries().into_iter().enumerate() {
|
||||
match entry {
|
||||
TreeEntry::File(p) => builder.leaf(id, label_for_path(p)),
|
||||
TreeEntry::Dir(p) => { builder.dir(id, label_for_path(p)); },
|
||||
TreeEntry::CloseDir => builder.close_dir()
|
||||
}
|
||||
}
|
||||
});
|
||||
actions
|
||||
}
|
||||
}
|
||||
|
||||
impl eframe::App for FleenUi {
|
||||
fn update(&mut self, ctx: &Context, frame: &mut Frame) {
|
||||
match &mut self.app {
|
||||
None => self.site_chooser(ctx, frame),
|
||||
Some(app) => self.display(ctx, frame)
|
||||
}
|
||||
|
||||
if let Some(err) = &self.error {
|
||||
let message = format!("{}", err);
|
||||
egui::Window::new("Error").collapsible(false).resizable(false).show(ctx, |ui| {
|
||||
ui.label(message);
|
||||
if ui.button("I see").clicked() {
|
||||
self.error = None
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn label_for_path(path: PathBuf) -> String {
|
||||
path.file_name().unwrap().to_string_lossy().to_string()
|
||||
}
|
||||
Reference in New Issue
Block a user