lots of stuff.

This commit is contained in:
rabite 2019-02-01 00:21:16 +01:00
parent 305d26d7b6
commit b7bbcff284
13 changed files with 473 additions and 466 deletions

View File

@ -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"

View File

@ -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
// }
}

View File

@ -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();
}

View File

@ -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)
}

View File

@ -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));
}
}
}
}

View File

@ -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();
}
}

View File

@ -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();

View File

@ -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()

View File

@ -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))
}

View File

@ -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()
}
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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
}