init
This commit is contained in:
commit
50f2c4255b
5 changed files with 3058 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/target
|
2854
Cargo.lock
generated
Normal file
2854
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
11
Cargo.toml
Normal file
11
Cargo.toml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
[package]
|
||||||
|
name = "file-gui"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
eframe = "0.22.0"
|
||||||
|
egui_extras = "0.22.0"
|
||||||
|
opener = "0.6.1"
|
1
rustfmt.toml
Normal file
1
rustfmt.toml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
hard_tabs = true
|
191
src/main.rs
Normal file
191
src/main.rs
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
use std::fs::DirEntry;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use eframe::{egui, Frame, NativeOptions};
|
||||||
|
use egui_extras::{Column, TableBuilder};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
eframe::run_native(
|
||||||
|
"File Exploder",
|
||||||
|
NativeOptions::default(),
|
||||||
|
Box::new(|cc| Box::new(FileGui::new(cc))),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FileGui {
|
||||||
|
current_dir: PathBuf,
|
||||||
|
loaded_dir: PathBuf,
|
||||||
|
path_input: String,
|
||||||
|
home: String,
|
||||||
|
entries: Vec<Entry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Entry {
|
||||||
|
name: String,
|
||||||
|
path: PathBuf,
|
||||||
|
filetype: FileType,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
enum FileType {
|
||||||
|
Dir,
|
||||||
|
File,
|
||||||
|
SymLink, // todo separate symlinks to directories and files
|
||||||
|
Unknown, // e.g. device files on linux
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileGui {
|
||||||
|
pub fn new(_cc: &eframe::CreationContext<'_>) -> Self {
|
||||||
|
let home = std::env::var("HOME").unwrap();
|
||||||
|
let current_dir = std::env::current_dir().unwrap();
|
||||||
|
let mut s = Self {
|
||||||
|
current_dir,
|
||||||
|
loaded_dir: PathBuf::new(),
|
||||||
|
path_input: String::new(),
|
||||||
|
home,
|
||||||
|
entries: Vec::new(),
|
||||||
|
};
|
||||||
|
s.override_input();
|
||||||
|
s.refresh_entries();
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
|
fn override_input(&mut self) {
|
||||||
|
self.path_input = self
|
||||||
|
.current_dir
|
||||||
|
.to_str()
|
||||||
|
.unwrap()
|
||||||
|
.replace('\\', "/")
|
||||||
|
.replace(&self.home, "~");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enter_input(&mut self) {
|
||||||
|
let path = self.path_input.replace("~", &self.home).replace('\\', "/");
|
||||||
|
self.current_dir = PathBuf::from(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn refresh_entries(&mut self) {
|
||||||
|
if !self.current_dir.is_dir() {
|
||||||
|
self.current_dir = self.loaded_dir.clone();
|
||||||
|
}
|
||||||
|
if !self.current_dir.is_dir() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.entries = self
|
||||||
|
.current_dir
|
||||||
|
.read_dir()
|
||||||
|
.unwrap()
|
||||||
|
.filter_map(|dir_entry| dir_entry.ok().map(Entry::new))
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
self.entries
|
||||||
|
.sort_by(|a, b| b.is_dir().cmp(&a.is_dir()).then(a.name.cmp(&b.name)));
|
||||||
|
self.loaded_dir = self.current_dir.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_parent(&mut self) {
|
||||||
|
if let Some(path) = self.current_dir.parent() {
|
||||||
|
self.current_dir = path.to_owned();
|
||||||
|
self.refresh_entries();
|
||||||
|
self.override_input();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open(&mut self, index: usize) {
|
||||||
|
let entry = &self.entries[index];
|
||||||
|
match entry.filetype {
|
||||||
|
FileType::Dir => {
|
||||||
|
self.current_dir = entry.path.clone();
|
||||||
|
self.refresh_entries();
|
||||||
|
self.override_input();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
opener::open(&entry.path).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl eframe::App for FileGui {
|
||||||
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut Frame) {
|
||||||
|
egui::TopBottomPanel::top("top_bar").show(ctx, |ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
if ui.button("⏶").clicked() {
|
||||||
|
self.to_parent();
|
||||||
|
}
|
||||||
|
if ui.button("⟳").clicked() {
|
||||||
|
self.refresh_entries();
|
||||||
|
}
|
||||||
|
let path_bar = ui.text_edit_singleline(&mut self.path_input);
|
||||||
|
if path_bar.lost_focus() {
|
||||||
|
if ui.input(|i| i.key_pressed(egui::Key::Enter)) {
|
||||||
|
self.enter_input();
|
||||||
|
self.refresh_entries();
|
||||||
|
} else if ui.input(|i| i.key_pressed(egui::Key::Escape)) {
|
||||||
|
self.override_input();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(filename) = self.loaded_dir.file_name() {
|
||||||
|
ui.label(filename.to_str().unwrap());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
|
TableBuilder::new(ui)
|
||||||
|
.striped(true)
|
||||||
|
.max_scroll_height(1200.0)
|
||||||
|
.column(Column::auto())
|
||||||
|
.column(Column::remainder())
|
||||||
|
.body(|body| {
|
||||||
|
let mut to_open = None;
|
||||||
|
body.rows(18.0, self.entries.len(), |row_index, mut row| {
|
||||||
|
let entry = &self.entries[row_index];
|
||||||
|
row.col(|ui| {
|
||||||
|
ui.label(match entry.filetype {
|
||||||
|
FileType::Dir => "🗁",
|
||||||
|
FileType::File => "🗋",
|
||||||
|
FileType::SymLink => "⮩",
|
||||||
|
FileType::Unknown => "?",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
row.col(|ui| {
|
||||||
|
if ui.button(&entry.name).clicked() {
|
||||||
|
to_open = Some(row_index);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if let Some(row_index) = to_open {
|
||||||
|
self.open(row_index);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Entry {
|
||||||
|
fn new(dir_entry: DirEntry) -> Option<Self> {
|
||||||
|
let name = dir_entry.file_name().into_string().ok()?;
|
||||||
|
let path = dir_entry.path();
|
||||||
|
let filetype = dir_entry.file_type().ok()?;
|
||||||
|
let filetype = if filetype.is_dir() {
|
||||||
|
FileType::Dir
|
||||||
|
} else if filetype.is_file() {
|
||||||
|
FileType::File
|
||||||
|
} else if filetype.is_symlink() {
|
||||||
|
FileType::SymLink
|
||||||
|
} else {
|
||||||
|
FileType::Unknown
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Self {
|
||||||
|
name,
|
||||||
|
path,
|
||||||
|
filetype,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_dir(&self) -> bool {
|
||||||
|
self.filetype == FileType::Dir
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue