mirror of https://github.com/bobwen-dev/hunter
lots of stuff.
This commit is contained in:
parent
305d26d7b6
commit
b7bbcff284
|
@ -11,4 +11,6 @@ lazy_static = "*"
|
|||
x11-clipboard = "*"
|
||||
alphanumeric-sort = "1.0.6"
|
||||
lscolors = { version = "0.5.0", features = [ "ansi_term" ] }
|
||||
mime-detective = "*"
|
||||
mime-detective = "*"
|
||||
rayon = "1.0.3"
|
||||
dirs-2 = "1.1.0"
|
|
@ -1,10 +1,9 @@
|
|||
#[derive(Debug,Clone,PartialEq)]
|
||||
pub struct Size(pub (u16,u16));
|
||||
#[derive(Debug,Clone,PartialEq)]
|
||||
pub struct Position(pub (u16,u16));
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Size(pub (u16, u16));
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Position(pub (u16, u16));
|
||||
|
||||
|
||||
#[derive(Debug,Clone,PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Coordinates {
|
||||
pub size: Size,
|
||||
pub position: Position,
|
||||
|
@ -12,12 +11,16 @@ pub struct Coordinates {
|
|||
|
||||
impl Coordinates {
|
||||
pub fn new() -> Coordinates {
|
||||
Coordinates { size: Size ((1,1)), position: Position ((1,1)) }
|
||||
}
|
||||
pub fn size(&self) -> &Size {
|
||||
&self.size
|
||||
Coordinates {
|
||||
size: Size((1, 1)),
|
||||
position: Position((1, 1)),
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn size(&self) -> &Size {
|
||||
// &self.size
|
||||
// }
|
||||
|
||||
pub fn xsize(&self) -> u16 {
|
||||
self.size.xsize()
|
||||
}
|
||||
|
@ -34,11 +37,11 @@ impl Coordinates {
|
|||
self.position().clone()
|
||||
}
|
||||
|
||||
// pub fn left(&self) -> /
|
||||
// pub fn left(&self) -> /
|
||||
}
|
||||
|
||||
impl Size {
|
||||
pub fn size(&self) -> (u16,u16) {
|
||||
pub fn size(&self) -> (u16, u16) {
|
||||
self.0
|
||||
}
|
||||
pub fn xsize(&self) -> u16 {
|
||||
|
@ -50,13 +53,13 @@ impl Size {
|
|||
}
|
||||
|
||||
impl Position {
|
||||
pub fn position(&self) -> (u16,u16) {
|
||||
pub fn position(&self) -> (u16, u16) {
|
||||
self.0
|
||||
}
|
||||
pub fn x(&self) -> u16 {
|
||||
(self.0).1
|
||||
}
|
||||
pub fn y(&self) -> u16 {
|
||||
(self.0).1
|
||||
}
|
||||
// pub fn y(&self) -> u16 {
|
||||
// (self.0).1
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -1,34 +1,35 @@
|
|||
use termion::event::{Key};
|
||||
use termion::event::Key;
|
||||
|
||||
use std::error::Error;
|
||||
use std::io::Write;
|
||||
|
||||
use crate::widget::Widget;
|
||||
use crate::coordinates::{Coordinates, Position, Size};
|
||||
use crate::files::Files;
|
||||
//use crate::hbox::HBox;
|
||||
use crate::listview::ListView;
|
||||
use crate::coordinates::{Size,Position,Coordinates};
|
||||
use crate::preview::Previewer;
|
||||
use crate::miller_columns::MillerColumns;
|
||||
use crate::widget::Widget;
|
||||
|
||||
pub struct FileBrowser {
|
||||
pub columns: MillerColumns<ListView<Files>>,
|
||||
}
|
||||
|
||||
impl FileBrowser {
|
||||
pub fn new() -> FileBrowser {
|
||||
pub fn new() -> Result<FileBrowser, Box<Error>> {
|
||||
let cwd = std::env::current_dir().unwrap();
|
||||
let mut miller = MillerColumns::new();
|
||||
|
||||
let mut lists: Vec<_> = cwd.ancestors().map(|path| {
|
||||
ListView::new(Files::new_from_path(path).unwrap())
|
||||
}).collect();
|
||||
let lists: Result<Vec<ListView<Files>>, Box<Error>> = cwd
|
||||
.ancestors()
|
||||
.map(|path| Ok(ListView::new(Files::new_from_path(path)?)))
|
||||
.collect();
|
||||
let mut lists = lists?;
|
||||
lists.reverse();
|
||||
|
||||
for widget in lists {
|
||||
miller.push_widget(widget);
|
||||
}
|
||||
|
||||
FileBrowser { columns: miller }
|
||||
Ok(FileBrowser { columns: miller })
|
||||
}
|
||||
|
||||
pub fn enter_dir(&mut self) {
|
||||
|
@ -37,30 +38,47 @@ impl FileBrowser {
|
|||
let path = fileview.selected_file().path();
|
||||
match Files::new_from_path(&path) {
|
||||
Ok(files) => {
|
||||
std::env::set_current_dir(path).unwrap();
|
||||
let view = ListView::new(files);
|
||||
self.columns.push_widget(view);
|
||||
self.update_preview();
|
||||
},
|
||||
Err(err) => {
|
||||
self.show_status(&format!("Can't open this path: {}", err));
|
||||
}
|
||||
Err(_) => {
|
||||
//self.show_status(&format!("Can't open this path: {}", err));
|
||||
|
||||
let status = std::process::Command::new("xdg-open")
|
||||
.args(dbg!(path.file_name()))
|
||||
.status();
|
||||
match status {
|
||||
Ok(status) => {
|
||||
self.show_status(&format!("\"{}\" exited with {}", "xdg-open", status))
|
||||
}
|
||||
Err(err) => {
|
||||
self.show_status(&format!("Can't run this \"{}\": {}", "xdg-open", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn go_back(&mut self) {
|
||||
if self.columns.get_left_widget().is_none() { return }
|
||||
if self.columns.get_left_widget().is_none() {
|
||||
return;
|
||||
}
|
||||
let fileview = self.columns.get_main_widget();
|
||||
let path = fileview.selected_file().grand_parent().unwrap();
|
||||
std::env::set_current_dir(path).unwrap();
|
||||
self.columns.pop_widget();
|
||||
|
||||
// Make sure there's a directory on the left unless it's /
|
||||
if self.columns.get_left_widget().is_none() {
|
||||
let file = self.columns.get_main_widget().selected_file().clone();
|
||||
if let Some(grand_parent) = file.grand_parent() {
|
||||
let left_view
|
||||
= ListView::new(Files::new_from_path(grand_parent).unwrap());
|
||||
let mut left_view = ListView::new(Files::new_from_path(&grand_parent).unwrap());
|
||||
left_view.select_file(&file);
|
||||
self.columns.prepend_widget(left_view);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn update_preview(&mut self) {
|
||||
|
@ -74,16 +92,15 @@ impl FileBrowser {
|
|||
let cwd = selected_file.path();
|
||||
let cwd = cwd.parent().unwrap();
|
||||
|
||||
let mut filepath = std::env::home_dir().unwrap();
|
||||
let mut filepath = dirs_2::home_dir().unwrap();
|
||||
filepath.push(".hunter_cwd");
|
||||
|
||||
let mut file = std::fs::File::create(filepath).unwrap();
|
||||
file.write(cwd.to_str().unwrap().as_bytes());
|
||||
file.write(cwd.to_str().unwrap().as_bytes()).unwrap();
|
||||
panic!("Quitting!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Widget for FileBrowser {
|
||||
fn render(&self) -> Vec<String> {
|
||||
vec![]
|
||||
|
@ -121,13 +138,12 @@ impl Widget for FileBrowser {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
fn on_key(&mut self, key: Key) {
|
||||
match key {
|
||||
Key::Char('Q') => self.quit_with_dir(),
|
||||
Key::Right => self.enter_dir(),
|
||||
Key::Left => self.go_back(),
|
||||
_ => self.columns.get_main_widget_mut().on_key(key)
|
||||
_ => self.columns.get_main_widget_mut().on_key(key),
|
||||
}
|
||||
self.update_preview();
|
||||
}
|
||||
|
|
152
src/files.rs
152
src/files.rs
|
@ -1,12 +1,12 @@
|
|||
use std::ops::Index;
|
||||
use std::error::Error;
|
||||
use std::path::PathBuf;
|
||||
use std::ffi::OsStr;
|
||||
use std::cmp::{Ord, Ordering};
|
||||
use std::error::Error;
|
||||
use std::ops::Index;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::SystemTime;
|
||||
|
||||
use lscolors::{LsColors, Style};
|
||||
use lscolors::LsColors;
|
||||
use mime_detective;
|
||||
use rayon::prelude::*;
|
||||
|
||||
lazy_static! {
|
||||
static ref COLORS: LsColors = LsColors::from_env().unwrap();
|
||||
|
@ -14,6 +14,7 @@ lazy_static! {
|
|||
|
||||
#[derive(PartialEq)]
|
||||
pub struct Files {
|
||||
pub directory: File,
|
||||
pub files: Vec<File>,
|
||||
pub sort: SortBy,
|
||||
pub dirs_first: bool,
|
||||
|
@ -28,40 +29,52 @@ impl Index<usize> for Files {
|
|||
|
||||
fn get_kind(file: &std::fs::DirEntry) -> Kind {
|
||||
let file = file.file_type().unwrap();
|
||||
if file.is_file() { return Kind::File; }
|
||||
if file.is_dir() { return Kind::Directory; }
|
||||
if file.is_symlink() { return Kind::Link; }
|
||||
if file.is_file() {
|
||||
return Kind::File;
|
||||
}
|
||||
if file.is_dir() {
|
||||
return Kind::Directory;
|
||||
}
|
||||
if file.is_symlink() {
|
||||
return Kind::Link;
|
||||
}
|
||||
Kind::Pipe
|
||||
}
|
||||
|
||||
fn get_color(path: &Path, meta: &std::fs::Metadata) -> Option<lscolors::Color> {
|
||||
match COLORS.style_for_path_with_metadata(path, Some(&meta)) {
|
||||
Some(style) => style.clone().foreground,
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
impl Files {
|
||||
pub fn new_from_path<S: AsRef<OsStr> + Sized>(path: S)
|
||||
-> Result<Files, Box<dyn Error>>
|
||||
where S: std::convert::AsRef<std::path::Path> {
|
||||
let mut files = Vec::new();
|
||||
pub fn new_from_path(path: &Path) -> Result<Files, Box<dyn Error>> {
|
||||
let direntries: Result<Vec<_>, _> = std::fs::read_dir(&path)?.collect();
|
||||
|
||||
for file in std::fs::read_dir(path)? {
|
||||
let file = file?;
|
||||
let name = file.file_name();
|
||||
let name = name.to_string_lossy();
|
||||
let kind = get_kind(&file);
|
||||
let path = file.path();
|
||||
let meta = file.metadata()?;
|
||||
let size = meta.len() / 1024;
|
||||
let mtime = meta.modified()?;
|
||||
let files: Vec<_> = direntries?
|
||||
.par_iter()
|
||||
.map(|file| {
|
||||
//let file = file?;
|
||||
let name = file.file_name();
|
||||
let name = name.to_string_lossy();
|
||||
let kind = get_kind(&file);
|
||||
let path = file.path();
|
||||
let meta = file.metadata().unwrap();
|
||||
let size = meta.len() / 1024;
|
||||
let mtime = meta.modified().unwrap();
|
||||
|
||||
let color
|
||||
= match COLORS.style_for_path_with_metadata(file.path(), Some(&meta)) {
|
||||
Some(style) => { style.clone().foreground },
|
||||
None => None
|
||||
};
|
||||
let file = File::new(&name, path, kind, size as usize, mtime, color);
|
||||
files.push(file)
|
||||
}
|
||||
let color = get_color(&path, &meta);
|
||||
File::new(&name, path, kind, size as usize, mtime, color)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut files = Files { files: files,
|
||||
sort: SortBy::Name,
|
||||
dirs_first: true };
|
||||
let mut files = Files {
|
||||
directory: File::new_from_path(&path)?,
|
||||
files: files,
|
||||
sort: SortBy::Name,
|
||||
dirs_first: true,
|
||||
};
|
||||
|
||||
files.sort();
|
||||
Ok(files)
|
||||
|
@ -69,23 +82,21 @@ impl Files {
|
|||
|
||||
pub fn sort(&mut self) {
|
||||
match self.sort {
|
||||
SortBy::Name => {
|
||||
self.files.sort_by(|a,b| {
|
||||
alphanumeric_sort::compare_str(&a.name, &b.name)
|
||||
})
|
||||
},
|
||||
SortBy::Name => self
|
||||
.files
|
||||
.sort_by(|a, b| alphanumeric_sort::compare_str(&a.name, &b.name)),
|
||||
SortBy::Size => {
|
||||
self.files.sort_by(|a,b| {
|
||||
self.files.sort_by(|a, b| {
|
||||
if a.size == b.size {
|
||||
return alphanumeric_sort::compare_str(&b.name, &a.name)
|
||||
return alphanumeric_sort::compare_str(&b.name, &a.name);
|
||||
}
|
||||
a.size.cmp(&b.size).reverse()
|
||||
});
|
||||
},
|
||||
}
|
||||
SortBy::MTime => {
|
||||
self.files.sort_by(|a,b| {
|
||||
self.files.sort_by(|a, b| {
|
||||
if a.mtime == b.mtime {
|
||||
return alphanumeric_sort::compare_str(&a.name, &b.name)
|
||||
return alphanumeric_sort::compare_str(&a.name, &b.name);
|
||||
}
|
||||
a.mtime.cmp(&b.mtime)
|
||||
});
|
||||
|
@ -93,10 +104,12 @@ impl Files {
|
|||
};
|
||||
|
||||
if self.dirs_first {
|
||||
self.files.sort_by(|a,b| {
|
||||
self.files.sort_by(|a, b| {
|
||||
if a.is_dir() && !b.is_dir() {
|
||||
Ordering::Less
|
||||
} else { Ordering::Equal }
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -105,14 +118,10 @@ impl Files {
|
|||
self.sort = match self.sort {
|
||||
SortBy::Name => SortBy::Size,
|
||||
SortBy::Size => SortBy::MTime,
|
||||
SortBy::MTime => SortBy::Name
|
||||
SortBy::MTime => SortBy::Name,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> std::slice::Iter<File> {
|
||||
self.files.iter()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.files.len()
|
||||
}
|
||||
|
@ -123,22 +132,21 @@ pub enum Kind {
|
|||
Directory,
|
||||
File,
|
||||
Link,
|
||||
Pipe
|
||||
Pipe,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SortBy {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_> )
|
||||
-> Result<(), std::fmt::Error> {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
let text = match self {
|
||||
SortBy::Name => "name",
|
||||
SortBy::Size => "size",
|
||||
SortBy::MTime => "mtime"
|
||||
SortBy::MTime => "mtime",
|
||||
};
|
||||
write!(formatter, "{}", text)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug,Copy,Clone,PartialEq)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum SortBy {
|
||||
Name,
|
||||
Size,
|
||||
|
@ -158,14 +166,15 @@ pub struct File {
|
|||
// flags: Option<String>,
|
||||
}
|
||||
|
||||
|
||||
impl File {
|
||||
pub fn new(name: &str,
|
||||
path: PathBuf,
|
||||
kind: Kind,
|
||||
size: usize,
|
||||
mtime: SystemTime,
|
||||
color: Option<lscolors::Color>) -> File {
|
||||
pub fn new(
|
||||
name: &str,
|
||||
path: PathBuf,
|
||||
kind: Kind,
|
||||
size: usize,
|
||||
mtime: SystemTime,
|
||||
color: Option<lscolors::Color>,
|
||||
) -> File {
|
||||
File {
|
||||
name: name.to_string(),
|
||||
path: path,
|
||||
|
@ -178,6 +187,22 @@ impl File {
|
|||
// flags: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_from_path(path: &Path) -> Result<File, Box<Error>> {
|
||||
let pathbuf = path.to_path_buf();
|
||||
let name = path
|
||||
.file_name()
|
||||
.map(|name| name.to_string_lossy().to_string())
|
||||
.unwrap_or("/".to_string());
|
||||
|
||||
let kind = Kind::Directory; //get_kind(&path);
|
||||
let meta = &path.metadata().unwrap();
|
||||
let size = meta.len() / 1024;
|
||||
let mtime = meta.modified().unwrap();
|
||||
let color = get_color(&path, meta);
|
||||
Ok(File::new(&name, pathbuf, kind, size as usize, mtime, color))
|
||||
}
|
||||
|
||||
pub fn calculate_size(&self) -> (usize, String) {
|
||||
let mut unit = 0;
|
||||
let mut size = self.size.unwrap();
|
||||
|
@ -191,8 +216,9 @@ impl File {
|
|||
2 => " GB",
|
||||
3 => " TB",
|
||||
4 => "wtf are you doing",
|
||||
_ => ""
|
||||
}.to_string();
|
||||
_ => "",
|
||||
}
|
||||
.to_string();
|
||||
(size, unit)
|
||||
}
|
||||
|
||||
|
|
181
src/listview.rs
181
src/listview.rs
|
@ -1,16 +1,20 @@
|
|||
use unicode_width::{UnicodeWidthStr};
|
||||
use termion::event::{Key,Event};
|
||||
use rayon::prelude::*;
|
||||
use termion::event::{Event, Key};
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::term;
|
||||
use crate::coordinates::{Coordinates, Position, Size};
|
||||
use crate::files::{File, Files};
|
||||
use crate::term;
|
||||
use crate::widget::Widget;
|
||||
use crate::coordinates::{Coordinates,Size,Position};
|
||||
|
||||
// Maybe also buffer drawlist for efficiency when it doesn't change every draw
|
||||
|
||||
pub struct ListView<T> {
|
||||
pub struct ListView<T>
|
||||
where
|
||||
T: Send,
|
||||
{
|
||||
pub content: T,
|
||||
selection: usize,
|
||||
offset: usize,
|
||||
|
@ -20,25 +24,26 @@ pub struct ListView<T> {
|
|||
coordinates: Coordinates,
|
||||
}
|
||||
|
||||
impl<T: 'static> ListView<T> where ListView<T>: Widget {
|
||||
impl<T> ListView<T>
|
||||
where
|
||||
ListView<T>: Widget,
|
||||
T: Send,
|
||||
{
|
||||
pub fn new(content: T) -> Self {
|
||||
let view = ListView::<T> {
|
||||
content: content,
|
||||
selection: 0,
|
||||
offset: 0,
|
||||
buffer: Vec::new(),
|
||||
coordinates: Coordinates { size: Size ((1,1)),
|
||||
position: Position((1,1)) }
|
||||
// dimensions: (1,1),
|
||||
// position: (1,1)
|
||||
coordinates: Coordinates {
|
||||
size: Size((1, 1)),
|
||||
position: Position((1, 1)),
|
||||
}, // dimensions: (1,1),
|
||||
// position: (1,1)
|
||||
};
|
||||
view
|
||||
}
|
||||
|
||||
pub fn to_trait(self) -> Box<Widget> {
|
||||
Box::new(self)
|
||||
}
|
||||
|
||||
fn move_up(&mut self) {
|
||||
if self.selection == 0 {
|
||||
return;
|
||||
|
@ -58,8 +63,7 @@ impl<T: 'static> ListView<T> where ListView<T>: Widget {
|
|||
return;
|
||||
}
|
||||
|
||||
if self.selection + 1 >= y_size && self.selection + 1 - self.offset >= y_size
|
||||
{
|
||||
if self.selection + 1 >= y_size && self.selection + 1 - self.offset >= y_size {
|
||||
self.offset += 1;
|
||||
}
|
||||
|
||||
|
@ -70,7 +74,9 @@ impl<T: 'static> ListView<T> where ListView<T>: Widget {
|
|||
let ysize = self.coordinates.ysize() as usize;
|
||||
let mut offset = 0;
|
||||
|
||||
while position + 1 > ysize + offset { offset += 1 }
|
||||
while position + 1 > ysize + offset {
|
||||
offset += 1
|
||||
}
|
||||
|
||||
self.offset = offset;
|
||||
self.selection = position;
|
||||
|
@ -82,19 +88,12 @@ impl<T: 'static> ListView<T> where ListView<T>: Widget {
|
|||
|
||||
let xsize = self.get_size().xsize();
|
||||
let sized_string = term::sized_string(&name, xsize);
|
||||
let padding = xsize as usize - sized_string.width();
|
||||
let padded_string = format!("{:padding$}",
|
||||
sized_string,
|
||||
padding = xsize as usize);
|
||||
let styled_string = match &file.color {
|
||||
Some(color) => format!("{}{}",
|
||||
term::from_lscolor(color),
|
||||
&padded_string),
|
||||
_ => format!("{}{}",
|
||||
term::normal_color(),
|
||||
padded_string)
|
||||
};
|
||||
|
||||
let padded_string = format!("{:padding$}", sized_string, padding = xsize as usize);
|
||||
let styled_string = match &file.color {
|
||||
Some(color) => format!("{}{}", term::from_lscolor(color), &padded_string),
|
||||
_ => format!("{}{}", term::normal_color(), padded_string),
|
||||
};
|
||||
|
||||
format!(
|
||||
"{}{}{}{}{}",
|
||||
|
@ -102,15 +101,16 @@ impl<T: 'static> ListView<T> where ListView<T>: Widget {
|
|||
term::highlight_color(),
|
||||
term::cursor_left(size.to_string().width() + unit.width()),
|
||||
size,
|
||||
unit)
|
||||
unit
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl ListView<Files> where
|
||||
impl ListView<Files>
|
||||
where
|
||||
ListView<Files>: Widget,
|
||||
Files: std::ops::Index<usize, Output=File>,
|
||||
Files: std::marker::Sized
|
||||
Files: std::ops::Index<usize, Output = File>,
|
||||
Files: std::marker::Sized,
|
||||
{
|
||||
pub fn selected_file(&self) -> &File {
|
||||
let selection = self.selection;
|
||||
|
@ -131,7 +131,7 @@ impl ListView<Files> where
|
|||
pub fn goto_grand_parent(&mut self) {
|
||||
match self.grand_parent() {
|
||||
Some(grand_parent) => self.goto_path(&grand_parent),
|
||||
None => self.show_status("Can't go further!")
|
||||
None => self.show_status("Can't go further!"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,13 +142,13 @@ impl ListView<Files> where
|
|||
}
|
||||
|
||||
pub fn goto_path(&mut self, path: &Path) {
|
||||
match crate::files::Files::new_from_path(path){
|
||||
match crate::files::Files::new_from_path(path) {
|
||||
Ok(files) => {
|
||||
self.content = files;
|
||||
self.selection = 0;
|
||||
self.offset = 0;
|
||||
self.refresh();
|
||||
},
|
||||
}
|
||||
Err(err) => {
|
||||
self.show_status(&format!("Can't open this path: {}", err));
|
||||
return;
|
||||
|
@ -156,8 +156,13 @@ impl ListView<Files> where
|
|||
}
|
||||
}
|
||||
|
||||
fn select_file(&mut self, file: &File) {
|
||||
let pos = self.content.files.iter().position(|item| item == file).unwrap();
|
||||
pub fn select_file(&mut self, file: &File) {
|
||||
let pos = self
|
||||
.content
|
||||
.files
|
||||
.par_iter()
|
||||
.position_any(|item| item == file)
|
||||
.unwrap();
|
||||
self.set_selection(pos);
|
||||
}
|
||||
|
||||
|
@ -182,28 +187,25 @@ impl ListView<Files> where
|
|||
fn exec_cmd(&mut self) {
|
||||
match self.minibuffer("exec ($s for selected files)") {
|
||||
Some(cmd) => {
|
||||
let filename = self.selected_file().name.clone();
|
||||
let cmd = cmd.replace("$s", &filename);
|
||||
self.show_status(&format!("Running: \"{}\"", &cmd));
|
||||
let mut parts = cmd.split_whitespace();
|
||||
let exe = parts.next().unwrap();
|
||||
let status = std::process::Command::new(exe).args(parts)
|
||||
.status();
|
||||
|
||||
let filename = self.selected_file().name.clone();
|
||||
let cmd = cmd.replace("$s", &format!("{}", &filename));
|
||||
|
||||
let status = std::process::Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(&cmd)
|
||||
.status();
|
||||
match status {
|
||||
Ok(status) => self.show_status(&format!("\"{}\" exited with {}",
|
||||
cmd,
|
||||
status)),
|
||||
Err(err) => self.show_status(&format!("Can't run this \"{}\": {}",
|
||||
cmd,
|
||||
err))
|
||||
Ok(status) => self.show_status(&format!("\"{}\" exited with {}", cmd, status)),
|
||||
Err(err) => self.show_status(&format!("Can't run this \"{}\": {}", cmd, err)),
|
||||
}
|
||||
},
|
||||
None => self.show_status("")
|
||||
}
|
||||
None => self.show_status(""),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Widget for ListView<Files> {
|
||||
fn get_size(&self) -> &Size {
|
||||
&self.coordinates.size
|
||||
|
@ -221,7 +223,9 @@ impl Widget for ListView<Files> {
|
|||
&self.coordinates
|
||||
}
|
||||
fn set_coordinates(&mut self, coordinates: &Coordinates) {
|
||||
if self.coordinates == *coordinates { return }
|
||||
if self.coordinates == *coordinates {
|
||||
return;
|
||||
}
|
||||
self.coordinates = coordinates.clone();
|
||||
self.refresh();
|
||||
}
|
||||
|
@ -229,32 +233,39 @@ impl Widget for ListView<Files> {
|
|||
self.buffer = self.render();
|
||||
}
|
||||
fn render(&self) -> Vec<String> {
|
||||
self.content.iter().map(|file| {
|
||||
self.render_line(&file)
|
||||
}).collect()
|
||||
self.content
|
||||
.files
|
||||
.par_iter()
|
||||
.map(|file| self.render_line(&file))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn get_drawlist(&self) -> String {
|
||||
let mut output = term::reset();
|
||||
let (xsize, ysize) = self.get_size().size();
|
||||
let (_, ysize) = self.get_size().size();
|
||||
let (xpos, ypos) = self.coordinates.position().position();
|
||||
|
||||
for (i, item) in self.buffer
|
||||
.iter()
|
||||
output += &self
|
||||
.buffer
|
||||
.par_iter()
|
||||
.skip(self.offset)
|
||||
.take(ysize as usize)
|
||||
.enumerate()
|
||||
{
|
||||
output += &term::normal_color();
|
||||
.map(|(i, item)| {
|
||||
let mut output = term::normal_color();
|
||||
|
||||
if i == (self.selection - self.offset) {
|
||||
output += &term::invert();
|
||||
}
|
||||
output += &format!("{}{}{}",
|
||||
term::goto_xy(xpos, i as u16 + ypos),
|
||||
item,
|
||||
term::reset());
|
||||
}
|
||||
if i == (self.selection - self.offset) {
|
||||
output += &term::invert();
|
||||
}
|
||||
output += &format!(
|
||||
"{}{}{}",
|
||||
term::goto_xy(xpos, i as u16 + ypos),
|
||||
item,
|
||||
term::reset()
|
||||
);
|
||||
String::from(output)
|
||||
})
|
||||
.collect::<String>();
|
||||
|
||||
output += &self.get_redraw_empty_list(self.buffer.len());
|
||||
|
||||
|
@ -266,18 +277,22 @@ impl Widget for ListView<Files> {
|
|||
|
||||
fn on_key(&mut self, key: Key) {
|
||||
match key {
|
||||
Key::Up => { self.move_up(); self.refresh(); },
|
||||
Key::Down => { self.move_down(); self.refresh(); },
|
||||
Key::Left => {
|
||||
self.goto_grand_parent()
|
||||
},
|
||||
Key::Right => {
|
||||
self.goto_selected()
|
||||
},
|
||||
Key::Char('s') => { self.cycle_sort() } ,
|
||||
Key::Char('d') => self.toggle_dirs_first() ,
|
||||
Key::Char('!') => self.exec_cmd() ,
|
||||
_ => { self.bad(Event::Key(key)); }
|
||||
Key::Up => {
|
||||
self.move_up();
|
||||
self.refresh();
|
||||
}
|
||||
Key::Down => {
|
||||
self.move_down();
|
||||
self.refresh();
|
||||
}
|
||||
Key::Left => self.goto_grand_parent(),
|
||||
Key::Right => self.goto_selected(),
|
||||
Key::Char('s') => self.cycle_sort(),
|
||||
Key::Char('d') => self.toggle_dirs_first(),
|
||||
Key::Char('!') => self.exec_cmd(),
|
||||
_ => {
|
||||
self.bad(Event::Key(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
96
src/main.rs
96
src/main.rs
|
@ -3,98 +3,40 @@ extern crate unicode_width;
|
|||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
extern crate alphanumeric_sort;
|
||||
extern crate dirs_2;
|
||||
extern crate lscolors;
|
||||
|
||||
use std::io::{stdout, Write};
|
||||
|
||||
|
||||
use termion::screen::AlternateScreen;
|
||||
extern crate mime_detective;
|
||||
extern crate rayon;
|
||||
|
||||
use termion::input::MouseTerminal;
|
||||
use termion::raw::IntoRawMode;
|
||||
use termion::screen::AlternateScreen;
|
||||
|
||||
use std::io::{stdout, Write};
|
||||
|
||||
mod term;
|
||||
mod window;
|
||||
mod listview;
|
||||
mod files;
|
||||
mod win_main;
|
||||
mod widget;
|
||||
//mod hbox;
|
||||
mod miller_columns;
|
||||
mod coordinates;
|
||||
|
||||
use listview::ListView;
|
||||
use window::Window;
|
||||
//use hbox::HBox;
|
||||
use miller_columns::MillerColumns;
|
||||
use widget::Widget;
|
||||
use coordinates::{Coordinates,Size,Position};
|
||||
mod file_browser;
|
||||
use file_browser::FileBrowser;
|
||||
mod files;
|
||||
mod listview;
|
||||
mod miller_columns;
|
||||
mod preview;
|
||||
mod term;
|
||||
mod textview;
|
||||
mod widget;
|
||||
mod win_main;
|
||||
mod window;
|
||||
|
||||
use window::Window;
|
||||
|
||||
fn main() {
|
||||
// Need to do this here to actually turn terminal into raw mode...
|
||||
let mut _screen = AlternateScreen::from(Box::new(stdout()));
|
||||
let mut _stdout = MouseTerminal::from(stdout().into_raw_mode().unwrap());
|
||||
|
||||
let (xsize, ysize) = term::size();
|
||||
let ysize = ysize - 1;
|
||||
let filebrowser = crate::file_browser::FileBrowser::new().unwrap();
|
||||
|
||||
let coordinates = Coordinates { size: Size ((xsize, ysize - 1)) ,
|
||||
position: Position( (1, 2 )) };
|
||||
|
||||
|
||||
let files = files::Files::new_from_path("/home/project/").unwrap();
|
||||
let mut listview = ListView::new(files);
|
||||
|
||||
let files = files::Files::new_from_path("/home/project/Downloads/").unwrap();
|
||||
let mut listview2 = ListView::new(files);
|
||||
|
||||
let files = files::Files::new_from_path("/home/").unwrap();
|
||||
let mut listview3 = ListView::new(files);
|
||||
|
||||
// let files = files::Files::new_from_path("/").unwrap();
|
||||
// let mut listview4 = ListView::new(files);
|
||||
|
||||
// listview.set_size( Size ((20,30)));
|
||||
// listview.set_position(Position((160,1)));
|
||||
|
||||
// listview2.set_size( Size ((20,30)));
|
||||
// listview2.set_position(Position((160,1)));
|
||||
|
||||
// listview3.set_size( Size ((20,30)));
|
||||
// listview3.set_position(Position((160,1)));
|
||||
|
||||
// listview4.set_size( Size ((20,30)));
|
||||
// listview4.set_position(Position((160,1)));
|
||||
|
||||
|
||||
// listview2.set_dimensions((95,53));
|
||||
// listview2.set_position((95,1));
|
||||
|
||||
// let boxed = vec![listview.to_trait(), listview2.to_trait()];
|
||||
|
||||
// let hbox = HBox::new(boxed, (xsize, ysize-1), (1,2) , 0);
|
||||
|
||||
|
||||
|
||||
let mut miller
|
||||
= MillerColumns::new(vec![listview3,listview,listview2],
|
||||
coordinates,
|
||||
(33, 33, 33));
|
||||
|
||||
// miller.main = Some(listview2);
|
||||
// miller.left = Some(listview3);
|
||||
// miller.preview = Some(listview4);
|
||||
|
||||
let coords = dbg!(miller.calculate_coordinates());
|
||||
|
||||
|
||||
miller.refresh();
|
||||
let filebrowser = crate::file_browser::FileBrowser { columns: miller };
|
||||
|
||||
let mut win = Window::new(filebrowser);
|
||||
win.handle_input();
|
||||
|
||||
write!(_stdout, "{}", termion::cursor::Show).unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,33 +1,30 @@
|
|||
use termion::event::{Key,Event};
|
||||
use termion::event::Key;
|
||||
|
||||
|
||||
use crate::widget::Widget;
|
||||
use crate::files::Files;
|
||||
//use crate::hbox::HBox;
|
||||
use crate::listview::ListView;
|
||||
use crate::coordinates::{Coordinates, Size,Position};
|
||||
use crate::files::File;
|
||||
use crate::coordinates::{Coordinates, Position, Size};
|
||||
use crate::preview::Previewer;
|
||||
use crate::widget::Widget;
|
||||
|
||||
pub struct MillerColumns<T> {
|
||||
pub widgets: Vec<T>,
|
||||
// pub left: Option<T>,
|
||||
// pub main: Option<T>,
|
||||
pub preview: Previewer,
|
||||
pub ratio: (u16,u16,u16),
|
||||
pub ratio: (u16, u16, u16),
|
||||
pub coordinates: Coordinates,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
impl<T> MillerColumns<T> where T: Widget {
|
||||
pub fn new() -> Self { Self { widgets: vec![],
|
||||
coordinates: Coordinates::new(),
|
||||
ratio: (33, 33, 33),
|
||||
preview: Previewer::new() } }
|
||||
|
||||
impl<T> MillerColumns<T>
|
||||
where
|
||||
T: Widget,
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
widgets: vec![],
|
||||
coordinates: Coordinates::new(),
|
||||
ratio: (33, 33, 33),
|
||||
preview: Previewer::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_widget(&mut self, mut widget: T) {
|
||||
let mcoords = self.calculate_coordinates().1;
|
||||
|
@ -55,43 +52,49 @@ impl<T> MillerColumns<T> where T: Widget {
|
|||
let ratio = self.ratio;
|
||||
|
||||
let left_xsize = xsize * ratio.0 / 100;
|
||||
let left_size = Size ((left_xsize, ysize));
|
||||
let left_size = Size((left_xsize, ysize));
|
||||
let left_pos = self.coordinates.top();
|
||||
|
||||
|
||||
let main_xsize = xsize * ratio.1 / 100;
|
||||
let main_size = Size ( (main_xsize, ysize) );
|
||||
let main_pos = Position ( (left_xsize + 2, top ));
|
||||
let main_size = Size((main_xsize, ysize));
|
||||
let main_pos = Position((left_xsize + 2, top));
|
||||
|
||||
let preview_xsize = xsize * ratio.2 / 100;
|
||||
let preview_size = Size ( (preview_xsize, ysize) );
|
||||
let preview_pos = Position ( (left_xsize + main_xsize + 3, top) );
|
||||
let preview_size = Size((preview_xsize, ysize));
|
||||
let preview_pos = Position((left_xsize + main_xsize + 3, top));
|
||||
|
||||
let left_coords = Coordinates { size: left_size,
|
||||
position: left_pos };
|
||||
let left_coords = Coordinates {
|
||||
size: left_size,
|
||||
position: left_pos,
|
||||
};
|
||||
|
||||
let main_coords = Coordinates {
|
||||
size: main_size,
|
||||
position: main_pos,
|
||||
};
|
||||
|
||||
let main_coords = Coordinates { size: main_size,
|
||||
position: main_pos };
|
||||
|
||||
|
||||
let preview_coords = Coordinates { size: preview_size,
|
||||
position: preview_pos };
|
||||
|
||||
let preview_coords = Coordinates {
|
||||
size: preview_size,
|
||||
position: preview_pos,
|
||||
};
|
||||
|
||||
(left_coords, main_coords, preview_coords)
|
||||
}
|
||||
|
||||
pub fn get_left_widget(&self) -> Option<&T> {
|
||||
let len = self.widgets.len();
|
||||
if len < 2 { return None }
|
||||
self.widgets.get(len-2)
|
||||
if len < 2 {
|
||||
return None;
|
||||
}
|
||||
self.widgets.get(len - 2)
|
||||
}
|
||||
pub fn get_left_widget_mut(&mut self) -> Option<&mut T> {
|
||||
let len = self.widgets.len();
|
||||
if len < 2 { return None }
|
||||
self.widgets.get(len-2)?.get_position();
|
||||
self.widgets.get_mut(len-2)
|
||||
if len < 2 {
|
||||
return None;
|
||||
}
|
||||
self.widgets.get(len - 2)?.get_position();
|
||||
self.widgets.get_mut(len - 2)
|
||||
}
|
||||
pub fn get_main_widget(&self) -> &T {
|
||||
self.widgets.last().unwrap()
|
||||
|
@ -101,7 +104,10 @@ impl<T> MillerColumns<T> where T: Widget {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> Widget for MillerColumns<T> where T: Widget {
|
||||
impl<T> Widget for MillerColumns<T>
|
||||
where
|
||||
T: Widget,
|
||||
{
|
||||
fn render(&self) -> Vec<String> {
|
||||
vec![]
|
||||
}
|
||||
|
@ -121,7 +127,9 @@ impl<T> Widget for MillerColumns<T> where T: Widget {
|
|||
&self.coordinates
|
||||
}
|
||||
fn set_coordinates(&mut self, coordinates: &Coordinates) {
|
||||
if self.coordinates == *coordinates { return }
|
||||
if self.coordinates == *coordinates {
|
||||
return;
|
||||
}
|
||||
self.coordinates = coordinates.clone();
|
||||
self.refresh();
|
||||
}
|
||||
|
@ -145,7 +153,7 @@ impl<T> Widget for MillerColumns<T> where T: Widget {
|
|||
fn get_drawlist(&self) -> String {
|
||||
let left_widget = match self.get_left_widget() {
|
||||
Some(widget) => widget.get_drawlist(),
|
||||
None => "".into()
|
||||
None => "".into(),
|
||||
};
|
||||
let main_widget = self.get_main_widget().get_drawlist();
|
||||
let preview = self.preview.get_drawlist();
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::widget::Widget;
|
||||
use crate::coordinates::{Coordinates, Size, Position};
|
||||
use crate::coordinates::{Coordinates, Position, Size};
|
||||
use crate::files::{File, Files, Kind};
|
||||
use crate::listview::ListView;
|
||||
use crate::textview::TextView;
|
||||
use crate::widget::Widget;
|
||||
|
||||
pub struct Previewer {
|
||||
pub file: Option<File>,
|
||||
|
@ -44,44 +44,49 @@ impl Widget for Previewer {
|
|||
&self.coordinates
|
||||
}
|
||||
fn set_coordinates(&mut self, coordinates: &Coordinates) {
|
||||
if self.coordinates == *coordinates { return }
|
||||
if self.coordinates == *coordinates {
|
||||
return;
|
||||
}
|
||||
self.coordinates = coordinates.clone();
|
||||
self.refresh();
|
||||
}
|
||||
fn render_header(&self) -> String { "".to_string() }
|
||||
fn render_header(&self) -> String {
|
||||
"".to_string()
|
||||
}
|
||||
fn refresh(&mut self) {
|
||||
if self.file == None { return }
|
||||
if self.file == None {
|
||||
return;
|
||||
}
|
||||
|
||||
let file = self.file.as_ref().unwrap();
|
||||
self.buffer =
|
||||
match &file.kind {
|
||||
Kind::Directory => {
|
||||
match Files::new_from_path(&file.path) {
|
||||
Ok(files) => {
|
||||
let len = files.len();
|
||||
let mut file_list = ListView::new(files);
|
||||
file_list.set_size(self.coordinates.size.clone());
|
||||
file_list.set_position(self.coordinates.position.clone());
|
||||
file_list.refresh();
|
||||
file_list.get_drawlist()
|
||||
+ &file_list.get_redraw_empty_list(len)
|
||||
}, Err(err) => {
|
||||
self.show_status(&format!("Can't preview because: {}", err));
|
||||
self.get_clearlist()
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
if file.get_mime() == Some("text".to_string()) {
|
||||
let mut textview = TextView::new_from_file(&file);
|
||||
textview.set_size(self.coordinates.size.clone());
|
||||
textview.set_position(self.coordinates.position.clone());
|
||||
textview.refresh();
|
||||
let len = textview.lines.len();
|
||||
textview.get_drawlist() + &textview.get_redraw_empty_list(len-1)
|
||||
} else { self.get_clearlist() }
|
||||
self.buffer = match &file.kind {
|
||||
Kind::Directory => match Files::new_from_path(&file.path) {
|
||||
Ok(files) => {
|
||||
let len = files.len();
|
||||
let mut file_list = ListView::new(files);
|
||||
file_list.set_size(self.coordinates.size.clone());
|
||||
file_list.set_position(self.coordinates.position.clone());
|
||||
file_list.refresh();
|
||||
file_list.get_drawlist() + &file_list.get_redraw_empty_list(len)
|
||||
}
|
||||
};
|
||||
Err(err) => {
|
||||
self.show_status(&format!("Can't preview because: {}", err));
|
||||
self.get_clearlist()
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
if file.get_mime() == Some("text".to_string()) {
|
||||
let mut textview = TextView::new_from_file(&file);
|
||||
textview.set_size(self.coordinates.size.clone());
|
||||
textview.set_position(self.coordinates.position.clone());
|
||||
textview.refresh();
|
||||
let len = textview.lines.len();
|
||||
textview.get_drawlist() + &textview.get_redraw_empty_list(len - 1)
|
||||
} else {
|
||||
self.get_clearlist()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
fn get_drawlist(&self) -> String {
|
||||
self.buffer.clone()
|
||||
|
|
42
src/term.rs
42
src/term.rs
|
@ -18,10 +18,6 @@ pub trait ScreenExt: Write {
|
|||
|
||||
impl ScreenExt for AlternateScreen<Box<Stdout>> {}
|
||||
|
||||
pub fn size() -> (u16, u16) {
|
||||
termion::terminal_size().unwrap()
|
||||
}
|
||||
|
||||
pub fn xsize() -> usize {
|
||||
let (xsize, _) = termion::terminal_size().unwrap();
|
||||
xsize as usize
|
||||
|
@ -33,16 +29,18 @@ pub fn ysize() -> usize {
|
|||
}
|
||||
|
||||
pub fn sized_string(string: &str, xsize: u16) -> String {
|
||||
let lenstr: String = string.chars().fold("".into(), |acc,ch| {
|
||||
if acc.width() + 1 >= xsize as usize { acc }
|
||||
else { acc + &ch.to_string() }
|
||||
let lenstr: String = string.chars().fold("".into(), |acc, ch| {
|
||||
if acc.width() + 1 >= xsize as usize {
|
||||
acc
|
||||
} else {
|
||||
acc + &ch.to_string()
|
||||
}
|
||||
});
|
||||
lenstr
|
||||
}
|
||||
|
||||
// Do these as constants
|
||||
|
||||
|
||||
pub fn highlight_color() -> String {
|
||||
format!(
|
||||
"{}",
|
||||
|
@ -61,27 +59,18 @@ pub fn normal_color() -> String {
|
|||
|
||||
pub fn from_lscolor(color: &lscolors::Color) -> String {
|
||||
match color {
|
||||
lscolors::Color::Black => {
|
||||
format!("{}", termion::color::Fg(termion::color::Black)) },
|
||||
lscolors::Color::Red => {
|
||||
format!("{}", termion::color::Fg(termion::color::Red)) }
|
||||
lscolors::Color::Green => {
|
||||
format!("{}",termion::color::Fg(termion::color::Green)) }
|
||||
lscolors::Color::Yellow => {
|
||||
format!("{}",termion::color::Fg(termion::color::Yellow)) }
|
||||
lscolors::Color::Blue => {
|
||||
format!("{}",termion::color::Fg(termion::color::Blue)) }
|
||||
lscolors::Color::Magenta => {
|
||||
format!("{}", termion::color::Fg(termion::color::Magenta)) }
|
||||
lscolors::Color::Cyan => {
|
||||
format!("{}",termion::color::Fg(termion::color::Cyan)) }
|
||||
lscolors::Color::White => {
|
||||
format!("{}",termion::color::Fg(termion::color::White)) } ,
|
||||
_ => { format!("{}", normal_color()) }
|
||||
lscolors::Color::Black => format!("{}", termion::color::Fg(termion::color::Black)),
|
||||
lscolors::Color::Red => format!("{}", termion::color::Fg(termion::color::Red)),
|
||||
lscolors::Color::Green => format!("{}", termion::color::Fg(termion::color::Green)),
|
||||
lscolors::Color::Yellow => format!("{}", termion::color::Fg(termion::color::Yellow)),
|
||||
lscolors::Color::Blue => format!("{}", termion::color::Fg(termion::color::Blue)),
|
||||
lscolors::Color::Magenta => format!("{}", termion::color::Fg(termion::color::Magenta)),
|
||||
lscolors::Color::Cyan => format!("{}", termion::color::Fg(termion::color::Cyan)),
|
||||
lscolors::Color::White => format!("{}", termion::color::Fg(termion::color::White)),
|
||||
_ => format!("{}", normal_color()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn cursor_left(n: usize) -> String {
|
||||
format!("{}", termion::cursor::Left(n as u16))
|
||||
}
|
||||
|
@ -121,4 +110,3 @@ pub fn header_color() -> String {
|
|||
pub fn status_bg() -> String {
|
||||
format!("{}", termion::color::Bg(termion::color::LightBlue))
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
use termion::event::{Key};
|
||||
use ::rayon::prelude::*;
|
||||
|
||||
use std::io::BufRead;
|
||||
|
||||
use crate::widget::Widget;
|
||||
use crate::coordinates::{Coordinates, Position, Size};
|
||||
use crate::files::File;
|
||||
use crate::coordinates::{Coordinates,Size,Position};
|
||||
use crate::term::sized_string;
|
||||
use crate::widget::Widget;
|
||||
|
||||
pub struct TextView {
|
||||
pub lines: Vec<String>,
|
||||
pub buffer: Vec<String>,
|
||||
pub buffer: String,
|
||||
pub coordinates: Coordinates,
|
||||
}
|
||||
|
||||
|
@ -17,11 +17,11 @@ impl TextView {
|
|||
pub fn new_from_file(file: &File) -> TextView {
|
||||
let file = std::fs::File::open(&file.path).unwrap();
|
||||
let file = std::io::BufReader::new(file);
|
||||
let lines = file.lines().take(100).map(|line| line.unwrap() ).collect();
|
||||
let lines = file.lines().take(100).map(|line| line.unwrap()).collect();
|
||||
|
||||
TextView {
|
||||
lines: lines,
|
||||
buffer: vec![],
|
||||
buffer: String::new(),
|
||||
coordinates: Coordinates::new(),
|
||||
}
|
||||
}
|
||||
|
@ -49,34 +49,30 @@ impl Widget for TextView {
|
|||
fn set_coordinates(&mut self, coordinates: &Coordinates) {
|
||||
self.coordinates = coordinates.clone();
|
||||
}
|
||||
fn render_header(&self) -> String { "".to_string() }
|
||||
fn refresh(&mut self) {
|
||||
let (xsize,ysize) = self.get_size().size();
|
||||
let (xpos, ypos) = self.get_position().position();
|
||||
|
||||
let lines = self.lines
|
||||
.iter()
|
||||
.take(ysize as usize)
|
||||
.map(|line| sized_string(&line, xsize)).collect();
|
||||
|
||||
|
||||
self.buffer = lines;
|
||||
fn render_header(&self) -> String {
|
||||
"".to_string()
|
||||
}
|
||||
fn get_drawlist(&self) -> String {
|
||||
let (xsize,ysize) = self.get_size().size();
|
||||
let (xpos, ypos) = self.get_position().position();
|
||||
fn refresh(&mut self) {
|
||||
let (xsize, ysize) = self.get_size().size();
|
||||
let (xpos, _) = self.get_position().position();
|
||||
|
||||
self.buffer
|
||||
.iter()
|
||||
self.buffer = self
|
||||
.lines
|
||||
.par_iter()
|
||||
.take(ysize as usize)
|
||||
.enumerate()
|
||||
.map(|(i, line)|{
|
||||
format!("{}{:xsize$}",
|
||||
crate::term::goto_xy(xpos,i as u16),
|
||||
line,
|
||||
xsize = xsize as usize)
|
||||
|
||||
}).collect()
|
||||
.map(|(i, line)| {
|
||||
format!(
|
||||
"{}{:xsize$}",
|
||||
crate::term::goto_xy(xpos, i as u16),
|
||||
sized_string(&line, xsize),
|
||||
xsize = xsize as usize
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
fn get_drawlist(&self) -> String {
|
||||
self.buffer.clone()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use termion::event::{Key, MouseEvent, Event};
|
||||
|
||||
use crate::coordinates::{Coordinates, Size, Position};
|
||||
use termion::event::{Event, Key, MouseEvent};
|
||||
|
||||
use crate::coordinates::{Coordinates, Position, Size};
|
||||
|
||||
pub trait Widget {
|
||||
fn render(&self) -> Vec<String>;
|
||||
|
@ -13,7 +12,6 @@ pub trait Widget {
|
|||
fn set_coordinates(&mut self, coordinates: &Coordinates);
|
||||
fn render_header(&self) -> String;
|
||||
|
||||
|
||||
fn on_event(&mut self, event: Event) {
|
||||
match event {
|
||||
Event::Key(Key::Char('q')) => panic!("It's your fault!"),
|
||||
|
@ -25,25 +23,19 @@ pub trait Widget {
|
|||
|
||||
fn on_key(&mut self, key: Key) {
|
||||
match key {
|
||||
_ => {
|
||||
self.bad(Event::Key(key))
|
||||
}
|
||||
_ => self.bad(Event::Key(key)),
|
||||
}
|
||||
}
|
||||
|
||||
fn on_mouse(&mut self, event: MouseEvent) {
|
||||
match event {
|
||||
_ => {
|
||||
self.bad(Event::Mouse(event))
|
||||
}
|
||||
_ => self.bad(Event::Mouse(event)),
|
||||
}
|
||||
}
|
||||
|
||||
fn on_wtf(&mut self, event: Vec<u8>) {
|
||||
match event {
|
||||
_ => {
|
||||
self.bad(Event::Unsupported(event))
|
||||
}
|
||||
_ => self.bad(Event::Unsupported(event)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,7 +54,7 @@ pub trait Widget {
|
|||
fn get_header_drawlist(&mut self) -> String {
|
||||
format!(
|
||||
"{}{}{}{:xsize$}",
|
||||
crate::term::goto_xy(1,1),
|
||||
crate::term::goto_xy(1, 1),
|
||||
crate::term::header_color(),
|
||||
self.render_header(),
|
||||
" ",
|
||||
|
@ -74,38 +66,36 @@ pub trait Widget {
|
|||
let (xpos, ypos) = self.get_position().position();
|
||||
let (xsize, ysize) = self.get_size().size();
|
||||
|
||||
let mut clearcmd = String::from("");
|
||||
for line in ypos..ysize+2 {
|
||||
clearcmd += &format!("{}{}{:xsize$}",
|
||||
crate::term::reset(),
|
||||
crate::term::goto_xy(xpos, line),
|
||||
" ",
|
||||
xsize=xsize as usize);
|
||||
}
|
||||
|
||||
clearcmd
|
||||
(ypos..ysize + 2)
|
||||
.map(|line| {
|
||||
format!(
|
||||
"{}{}{:xsize$}",
|
||||
crate::term::reset(),
|
||||
crate::term::goto_xy(xpos, line),
|
||||
" ",
|
||||
xsize = xsize as usize
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn get_redraw_empty_list(&self, lines: usize) -> String {
|
||||
let (xpos, ypos) = self.get_position().position();
|
||||
let (xsize, ysize) = self.get_size().size();
|
||||
|
||||
let mut output = String::new();
|
||||
|
||||
if ysize as usize > lines {
|
||||
let start_y = lines + ypos as usize;
|
||||
for i in start_y..(ysize+2) as usize {
|
||||
output += &format!("{}{:xsize$}",
|
||||
crate::term::goto_xy(xpos,i as u16),
|
||||
" ",
|
||||
xsize = xsize as usize);
|
||||
}
|
||||
}
|
||||
|
||||
output
|
||||
let start_y = lines + ypos as usize;
|
||||
(start_y..(ysize + 2) as usize)
|
||||
.map(|i| {
|
||||
format!(
|
||||
"{}{:xsize$}",
|
||||
crate::term::goto_xy(xpos, i as u16),
|
||||
" ",
|
||||
xsize = xsize as usize
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
||||
fn refresh(&mut self);
|
||||
fn get_drawlist(&self) -> String;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
//use crate::listview::ListView;
|
||||
|
||||
|
||||
// struct MainWindow {
|
||||
// active_widget: usize,
|
||||
// main: ListView<Files>,
|
||||
|
@ -17,7 +16,7 @@
|
|||
// // let name = &self.content.content[pos].name.clone();
|
||||
// // let path = &self.content.content[pos].path.clone();
|
||||
// // let newfiles = crate::files::get_files(path).unwrap();
|
||||
|
||||
|
||||
// // let listview = ListView::new(newfiles, (80,80), (10,10));
|
||||
|
||||
// // let mut win = Window::new(listview);
|
||||
|
|
|
@ -8,11 +8,12 @@ use termion::screen::AlternateScreen;
|
|||
use crate::term;
|
||||
use crate::term::ScreenExt;
|
||||
|
||||
use crate::coordinates::{Coordinates, Position, Size};
|
||||
use crate::widget::Widget;
|
||||
use crate::coordinates::{Coordinates, Size, Position};
|
||||
|
||||
pub struct Window<T>
|
||||
where T: Widget
|
||||
where
|
||||
T: Widget,
|
||||
{
|
||||
pub selection: usize,
|
||||
pub widget: T,
|
||||
|
@ -21,10 +22,9 @@ where T: Widget
|
|||
pub coordinates: Coordinates,
|
||||
}
|
||||
|
||||
|
||||
impl<T> Window<T>
|
||||
where
|
||||
T: Widget
|
||||
T: Widget,
|
||||
{
|
||||
pub fn new(widget: T) -> Window<T> {
|
||||
let mut screen = AlternateScreen::from(Box::new(stdout()));
|
||||
|
@ -35,12 +35,16 @@ where
|
|||
widget: widget,
|
||||
status: STATUS_BAR_CONTENT.clone(),
|
||||
screen: screen,
|
||||
coordinates: Coordinates { size: Size ((xsize, ysize)) ,
|
||||
position: Position( (1, 1 )) }
|
||||
coordinates: Coordinates {
|
||||
size: Size((xsize, ysize)),
|
||||
position: Position((1, 1)),
|
||||
},
|
||||
};
|
||||
|
||||
win.widget.set_coordinates( &Coordinates { size: Size ((xsize, ysize - 2)),
|
||||
position: Position( (1, 2)) });
|
||||
win.widget.set_coordinates(&Coordinates {
|
||||
size: Size((xsize, ysize - 2)),
|
||||
position: Position((1, 2)),
|
||||
});
|
||||
win.widget.refresh();
|
||||
win
|
||||
}
|
||||
|
@ -65,8 +69,6 @@ where
|
|||
Self::show_status("");
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub fn handle_input(&mut self) {
|
||||
self.draw();
|
||||
for event in stdin().events() {
|
||||
|
@ -82,16 +84,23 @@ where
|
|||
|
||||
impl<T> Drop for Window<T>
|
||||
where
|
||||
T: Widget
|
||||
T: Widget,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
// When done, restore the defaults to avoid messing with the terminal.
|
||||
self.screen.write(format!("{}{}{}{}{}",
|
||||
termion::screen::ToMainScreen,
|
||||
termion::clear::All,
|
||||
termion::style::Reset,
|
||||
termion::cursor::Show,
|
||||
termion::cursor::Goto(1, 1)).as_ref()).unwrap();
|
||||
self.screen
|
||||
.write(
|
||||
format!(
|
||||
"{}{}{}{}{}",
|
||||
termion::screen::ToMainScreen,
|
||||
termion::clear::All,
|
||||
termion::style::Reset,
|
||||
termion::cursor::Show,
|
||||
termion::cursor::Goto(1, 1)
|
||||
)
|
||||
.as_ref(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,17 +122,18 @@ pub fn draw_status() {
|
|||
term::move_bottom(),
|
||||
status,
|
||||
xsize = xsize as usize
|
||||
).ok()
|
||||
)
|
||||
.ok()
|
||||
});
|
||||
stdout().flush().unwrap();
|
||||
}
|
||||
|
||||
pub fn show_status(status: &str) {
|
||||
{
|
||||
let mut status_content = STATUS_BAR_CONTENT.try_lock().unwrap();
|
||||
*status_content = Some(status.to_string());
|
||||
}
|
||||
draw_status();
|
||||
{
|
||||
let mut status_content = STATUS_BAR_CONTENT.try_lock().unwrap();
|
||||
*status_content = Some(status.to_string());
|
||||
}
|
||||
draw_status();
|
||||
}
|
||||
|
||||
pub fn minibuffer(query: &str) -> Option<String> {
|
||||
|
@ -132,19 +142,26 @@ pub fn minibuffer(query: &str) -> Option<String> {
|
|||
|
||||
for key in stdin().events() {
|
||||
match key {
|
||||
Ok(Event::Key(key)) => {
|
||||
match key {
|
||||
Key::Esc => { return None },
|
||||
Key::Char('\n') => { return Some(buffer) },
|
||||
Key::Backspace => { buffer.pop(); },
|
||||
Key::Char(key) => { buffer = buffer + &format!("{}", key); },
|
||||
_ => {}
|
||||
Ok(Event::Key(key)) => match key {
|
||||
Key::Esc => return None,
|
||||
Key::Char('\n') => {
|
||||
if buffer == "" {
|
||||
return None;
|
||||
} else {
|
||||
return Some(buffer);
|
||||
}
|
||||
}
|
||||
Key::Backspace => {
|
||||
buffer.pop();
|
||||
}
|
||||
Key::Char(key) => {
|
||||
buffer = buffer + &format!("{}", key);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
show_status(&(query.to_string() + ": " + &buffer));
|
||||
|
||||
};
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue