From 5d456539015876c1891bf0ac05b8f7dd3fbe8d57 Mon Sep 17 00:00:00 2001 From: rabite Date: Sat, 9 Mar 2019 11:06:13 +0100 Subject: [PATCH] use enum to allow multiple widget types in hbox --- src/fail.rs | 16 +- src/file_browser.rs | 334 +++++++++++++++++++++++++++--------------- src/files.rs | 21 +++ src/hbox.rs | 91 ++++++++---- src/miller_columns.rs | 86 +++-------- src/minibuffer.rs | 2 - src/preview.rs | 72 +++++++-- src/proclist.rs | 154 +++++++++++-------- src/tabview.rs | 3 + src/widget.rs | 21 ++- 10 files changed, 501 insertions(+), 299 deletions(-) diff --git a/src/fail.rs b/src/fail.rs index a6f8abe..2c01885 100644 --- a/src/fail.rs +++ b/src/fail.rs @@ -45,13 +45,27 @@ pub enum HError { #[fail(display = "No header for widget")] NoHeaderError, #[fail(display = "You wanted this!")] - Quit + Quit, + #[fail(display = "HBox ratio mismatch: {} widgets, ratio is {:?}", wnum, ratio)] + HBoxWrongRatioError{ wnum: usize, ratio: Vec }, + #[fail(display = "Got wrong widget: {}! Wanted: {}", got, wanted)] + WrongWidgetError{got: String, wanted: String}, } impl HError { pub fn quit() -> HResult<()> { Err(HError::Quit) } + pub fn wrong_ratio(wnum: usize, ratio: Vec) -> HResult { + Err(HError::HBoxWrongRatioError{ wnum: wnum, ratio: ratio }) + } + pub fn no_widget() -> HResult { + Err(HError::NoWidgetError(Backtrace::new())) + } + pub fn wrong_widget(got: &str, wanted: &str) -> HResult { + Err(HError::WrongWidgetError{ got: got.to_string(), + wanted: wanted.to_string()}) + } } pub trait ErrorLog where Self: Sized { diff --git a/src/file_browser.rs b/src/file_browser.rs index 37a836c..fb8fea2 100644 --- a/src/file_browser.rs +++ b/src/file_browser.rs @@ -6,22 +6,55 @@ use std::sync::{Arc, Mutex}; use std::sync::mpsc::{channel, Receiver, Sender}; use std::time::Duration; use std::path::PathBuf; +use std::collections::HashMap; use crate::files::{File, Files}; use crate::listview::ListView; use crate::miller_columns::MillerColumns; use crate::widget::Widget; use crate::tabview::{TabView, Tabbable}; -use crate::preview::WillBeWidget; +use crate::preview::{Previewer, WillBeWidget}; use crate::fail::{HResult, HError, ErrorLog}; use crate::widget::{Events, WidgetCore}; use crate::proclist::ProcView; +#[derive(PartialEq)] +pub enum FileBrowserWidgets { + FileList(WillBeWidget>), + Previewer(Previewer), +} +impl Widget for FileBrowserWidgets { + fn get_core(&self) -> HResult<&WidgetCore> { + match self { + FileBrowserWidgets::FileList(widget) => widget.get_core(), + FileBrowserWidgets::Previewer(widget) => widget.get_core() + } + } + fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> { + match self { + FileBrowserWidgets::FileList(widget) => widget.get_core_mut(), + FileBrowserWidgets::Previewer(widget) => widget.get_core_mut() + } + } + fn refresh(&mut self) -> HResult<()> { + match self { + FileBrowserWidgets::FileList(widget) => widget.refresh(), + FileBrowserWidgets::Previewer(widget) => widget.refresh() + } + } + fn get_drawlist(&self) -> HResult { + match self { + FileBrowserWidgets::FileList(widget) => widget.get_drawlist(), + FileBrowserWidgets::Previewer(widget) => widget.get_drawlist() + } + } +} pub struct FileBrowser { - pub columns: MillerColumns>>, + pub columns: MillerColumns, pub cwd: File, + selections: HashMap, core: WidgetCore, watcher: INotifyWatcher, watches: Vec, @@ -105,15 +138,17 @@ fn watch_dir(rx: Receiver, impl FileBrowser { pub fn new_cored(core: &WidgetCore) -> HResult { let cwd = std::env::current_dir().unwrap(); - let coords = core.coordinates.clone(); - let core_m = core.clone(); - let core_l = core.clone(); + let mut core_m = core.clone(); + let mut core_l = core.clone(); + let mut core_p = core.clone(); let mut miller = MillerColumns::new(core); - miller.set_coordinates(&coords)?; + miller.set_ratios(vec![20,30,49]); + let list_coords = miller.calculate_coordinates()?; - - let (left_coords, main_coords, _) = miller.calculate_coordinates(); + core_l.coordinates = list_coords[0].clone(); + core_m.coordinates = list_coords[1].clone(); + core_p.coordinates = list_coords[2].clone(); let main_path = cwd.ancestors() .take(1) @@ -125,7 +160,6 @@ impl FileBrowser { let main_widget = WillBeWidget::new(&core, Box::new(move |_| { let mut list = ListView::new(&core_m, Files::new_from_path(&main_path)?); - list.set_coordinates(&main_coords).log(); list.animate_slide_up().log(); Ok(list) })); @@ -134,14 +168,18 @@ impl FileBrowser { let left_widget = WillBeWidget::new(&core, Box::new(move |_| { let mut list = ListView::new(&core_l, Files::new_from_path(&left_path)?); - list.set_coordinates(&left_coords).log(); list.animate_slide_up().log(); Ok(list) })); + let left_widget = FileBrowserWidgets::FileList(left_widget); miller.push_widget(left_widget); } - miller.push_widget(main_widget); + let previewer = Previewer::new(&core_p); + + miller.push_widget(FileBrowserWidgets::FileList(main_widget)); + miller.push_widget(FileBrowserWidgets::Previewer(previewer)); + miller.refresh().log(); let cwd = File::new_from_path(&cwd).unwrap(); @@ -151,11 +189,13 @@ impl FileBrowser { let watcher = INotifyWatcher::new(tx_watch, Duration::from_secs(2)).unwrap(); watch_dir(rx_watch, dir_events.clone(), core.get_sender()); - let mut proc_view = ProcView::new(core); - proc_view.set_coordinates(&coords).log(); + let proc_view = ProcView::new(core); + + Ok(FileBrowser { columns: miller, cwd: cwd, + selections: HashMap::new(), core: core.clone(), watcher: watcher, watches: vec![], @@ -165,112 +205,138 @@ impl FileBrowser { pub fn enter_dir(&mut self) -> HResult<()> { let file = self.selected_file()?; - let (_, coords, _) = self.columns.calculate_coordinates(); - let core = self.core.clone(); - match file.read_dir() { - Ok(files) => { - std::env::set_current_dir(&file.path).unwrap(); - self.cwd = file.clone(); - let view = WillBeWidget::new(&core.clone(), Box::new(move |_| { - let files = files.clone(); - let mut list = ListView::new(&core, files); - list.set_coordinates(&coords).log(); - list.animate_slide_up().log(); - Ok(list) - })); - self.columns.push_widget(view); - }, - _ => { - let status = std::process::Command::new("rifle") - .args(file.path.file_name()) - .status(); + if file.is_dir() { + self.main_widget_goto(&file).log(); + } else { + let status = std::process::Command::new("rifle") + .args(file.path.file_name()) + .status(); - match status { - Ok(status) => - self.show_status(&format!("\"{}\" exited with {}", - "rifle", status)).log(), - Err(err) => - self.show_status(&format!("Can't run this \"{}\": {}", - "rifle", err)).log() - - }; + match status { + Ok(status) => + self.show_status(&format!("\"{}\" exited with {}", + "rifle", status)).log(), + Err(err) => + self.show_status(&format!("Can't run this \"{}\": {}", + "rifle", err)).log() } } Ok(()) } - pub fn go_back(&mut self) -> HResult<()> { - self.columns.pop_widget(); + pub fn main_widget_goto(&mut self, dir: &File) -> HResult<()> { + self.cwd = dir.clone(); + let dir = dir.clone(); + let selected_file = self.get_selection(&dir).ok().cloned(); - if let Ok(new_cwd) = self.cwd.grand_parent_as_file() { - self.cwd = new_cwd; + let main_widget = self.main_widget_mut()?; + main_widget.change_to(Box::new(move |stale, core| { + let path = dir.path(); + let files = Files::new_from_path_cancellable(&path, stale)?; + + let mut list = ListView::new(&core, files); + + if let Some(file) = &selected_file { + list.select_file(file); + } + Ok(list) + })).log(); + + if let Ok(grand_parent) = self.cwd()?.parent_as_file() { + self.left_widget_goto(&grand_parent).log(); + } else { + self.left_widget_mut()?.set_stale().log(); + } + + Ok(()) + } + + pub fn left_widget_goto(&mut self, dir: &File) -> HResult<()> { + let dir = dir.clone(); + + let left_widget = self.left_widget_mut()?; + left_widget.change_to(Box::new(move |stale, core| { + let path = dir.path(); + let files = Files::new_from_path_cancellable(&path, stale)?; + let list = ListView::new(&core, files); + Ok(list) + }))?; + Ok(()) + } + + pub fn go_back(&mut self) -> HResult<()> { + if let Ok(new_cwd) = self.cwd.parent_as_file() { + self.main_widget_goto(&new_cwd).log(); } self.refresh() } pub fn update_preview(&mut self) -> HResult<()> { + if !self.main_widget()?.ready() { return Ok(()) } let file = self.selected_file()?.clone(); - let preview = &mut self.columns.preview; - preview.set_file(&file); + let selection = self.get_selection(&file).ok().cloned(); + let preview = self.preview_widget_mut()?; + preview.set_file(&file, selection); Ok(()) } - pub fn fix_selection(&mut self) -> HResult<()> { - let cwd = self.cwd()?; - (*self.left_widget()?.lock()?).as_mut()?.select_file(&cwd); + pub fn set_left_selection(&mut self) -> HResult<()> { + if !self.left_widget()?.ready() { return Ok(()) } + + let parent = self.cwd()?.parent_as_file(); + + let left_selection = self.get_selection(&parent?)?; + self.left_widget()?.widget()?.lock()?.as_mut()?.select_file(&left_selection); + Ok(()) } - pub fn fix_left(&mut self) -> HResult<()> { - if self.left_widget().is_err() { - let cwd = self.selected_file()?.clone(); - if let Ok(grand_parent) = cwd.grand_parent_as_file() { - let (coords, _, _) = self.columns.calculate_coordinates(); - let core = self.core.clone(); - let left_view = WillBeWidget::new(&self.core, Box::new(move |_| { - let mut view - = ListView::new(&core, - Files::new_from_path(&grand_parent.path)?); - view.set_coordinates(&coords).log(); - Ok(view) - })); - self.columns.prepend_widget(left_view); - } + pub fn get_selection(&self, dir: &File) -> HResult<&File> { + Ok(self.selections.get(dir)?) + } + + pub fn save_selection(&mut self) -> HResult<()> { + let cwd = self.cwd()?.clone(); + if let Ok(main_selection) = self.selected_file() { + self.selections.insert(cwd.clone(), main_selection); + } + if let Ok(left_dir) = self.cwd()?.parent_as_file() { + self.selections.insert(left_dir, cwd); } Ok(()) } - pub fn cwd(&self) -> HResult { - let widget = self.columns.get_main_widget()?.widget()?; - let cwd = (*widget.lock()?).as_ref()?.content.directory.clone(); - Ok(cwd) + pub fn cwd(&self) -> HResult<&File> { + Ok(&self.cwd) } pub fn set_cwd(&mut self) -> HResult<()> { let cwd = self.cwd()?; std::env::set_current_dir(&cwd.path)?; - self.cwd = cwd; Ok(()) } pub fn left_dir(&self) -> HResult { - let widget = self.columns.get_left_widget()?.widget()?; - let dir = (*widget.lock()?).as_ref()?.content.directory.clone(); + let widget = self.left_widget()?.widget()?; + let dir = widget.lock()?.as_ref()?.content.directory.clone(); Ok(dir) } fn update_watches(&mut self) -> HResult<()> { + if !self.left_widget()?.ready() || !self.main_widget()?.ready() { + return Ok(()) + } let watched_dirs = self.watches.clone(); - let cwd = self.cwd()?; + let cwd = self.cwd()?.clone(); let left_dir = self.left_dir()?; let preview_dir = self.selected_file().ok().map(|f| f.path); for watched_dir in watched_dirs.iter() { if watched_dir != &cwd.path && watched_dir != &left_dir.path && Some(watched_dir.clone()) != preview_dir { - self.watcher.unwatch(&watched_dir).unwrap(); + self.watcher.unwatch(&watched_dir).ok(); self.watches.remove_item(&watched_dir); } } @@ -292,51 +358,85 @@ impl FileBrowser { } fn handle_dir_events(&mut self) -> HResult<()> { - let mut dir_events = self.dir_events.lock()?; - for event in dir_events.iter() { - let main_widget = self.columns.get_main_widget()?.widget()?; - let main_files = &mut (*main_widget.lock()?); - let main_files = &mut main_files.as_mut()?.content; - let main_result = main_files.handle_event(event); + let dir_events = self.dir_events.clone(); + for event in dir_events.lock()?.iter() { + let main_widget = self.main_widget()?.widget()?; + let mut main_widget = main_widget.lock()?; + let main_result = main_widget.as_mut()?.content.handle_event(event); - let left_widget = self.columns.get_left_widget()?.widget()?; - let left_files = &mut (*left_widget.lock()?); - let left_files = &mut left_files.as_mut()?.content; - let left_result = left_files.handle_event(event); + let left_widget = self.left_widget()?.widget()?; + let mut left_files = left_widget.lock()?; + let left_result = left_files.as_mut()?.content.handle_event(event); match main_result { Err(HError::WrongDirectoryError { .. }) => { match left_result { Err(HError::WrongDirectoryError { .. }) => { - let preview = &mut self.columns.preview; + let preview = self.preview_widget_mut()?; preview.reload(); }, _ => {} } }, _ => {} } } - dir_events.clear(); + dir_events.lock()?.clear(); Ok(()) } pub fn selected_file(&self) -> HResult { - let widget = self.main_widget()?; + let widget = self.main_widget()?.widget()?; let file = widget.lock()?.as_ref()?.selected_file().clone(); Ok(file) } - pub fn main_widget(&self) -> HResult>>>> { - let widget = self.columns.get_main_widget()?.widget()?; - Ok(widget) + pub fn main_widget(&self) -> HResult<&WillBeWidget>> { + let widget = match self.columns.get_main_widget()? { + FileBrowserWidgets::FileList(filelist) => Ok(filelist), + _ => { return HError::wrong_widget("previewer", "filelist"); } + }; + widget } - pub fn left_widget(&self) -> HResult>>>> { - let widget = self.columns.get_left_widget()?.widget()?; - Ok(widget) + pub fn main_widget_mut(&mut self) -> HResult<&mut WillBeWidget>> { + let widget = match self.columns.get_main_widget_mut()? { + FileBrowserWidgets::FileList(filelist) => Ok(filelist), + _ => { return HError::wrong_widget("previewer", "filelist"); } + }; + widget + } + + pub fn left_widget(&self) -> HResult<&WillBeWidget>> { + let widget = match self.columns.get_left_widget()? { + FileBrowserWidgets::FileList(filelist) => Ok(filelist), + _ => { return HError::wrong_widget("previewer", "filelist"); } + }; + widget + } + + pub fn left_widget_mut(&mut self) -> HResult<&mut WillBeWidget>> { + let widget = match self.columns.get_left_widget_mut()? { + FileBrowserWidgets::FileList(filelist) => Ok(filelist), + _ => { return HError::wrong_widget("previewer", "filelist"); } + }; + widget + } + + pub fn preview_widget(&self) -> HResult<&Previewer> { + match self.columns.get_right_widget()? { + FileBrowserWidgets::Previewer(previewer) => Ok(previewer), + _ => { return HError::wrong_widget("filelist", "previewer"); } + } + } + + pub fn preview_widget_mut(&mut self) -> HResult<&mut Previewer> { + match self.columns.get_right_widget_mut()? { + FileBrowserWidgets::Previewer(previewer) => Ok(previewer), + _ => { return HError::wrong_widget("filelist", "previewer"); } + } } pub fn quit_with_dir(&self) -> HResult<()> { - let cwd = self.cwd()?.path; + let cwd = self.cwd()?.clone().path; let selected_file = self.selected_file()?; let selected_file = selected_file.path.to_string_lossy(); @@ -362,22 +462,22 @@ impl FileBrowser { self.cwd = cwd; let dir = std::path::PathBuf::from(&dir); let left_dir = std::path::PathBuf::from(&dir); - let (left_coords, main_coords, _) = self.columns.calculate_coordinates(); - let mcore = self.core.clone(); - let lcore = self.core.clone(); + let mcore = self.main_widget()?.get_core()?.clone(); + let lcore = self.left_widget()?.get_core()?.clone();; let middle = WillBeWidget::new(&self.core, Box::new(move |_| { let files = Files::new_from_path(&dir.clone())?; - let mut listview = ListView::new(&mcore, files); - listview.set_coordinates(&main_coords).log(); + let listview = ListView::new(&mcore, files); Ok(listview) })); + let middle = FileBrowserWidgets::FileList(middle); + let left = WillBeWidget::new(&self.core, Box::new(move |_| { let files = Files::new_from_path(&left_dir.parent()?)?; - let mut listview = ListView::new(&lcore, files); - listview.set_coordinates(&left_coords).log(); + let listview = ListView::new(&lcore, files); Ok(listview) })); + let left = FileBrowserWidgets::FileList(left); self.columns.push_widget(left); self.columns.push_widget(middle); }, @@ -388,9 +488,9 @@ impl FileBrowser { fn exec_cmd(&mut self, tab_dirs: Vec) -> HResult<()> { let filename = self.selected_file()?.name.clone(); - let widget = self.main_widget()?; + let widget = self.main_widget()?.widget()?; let widget = widget.lock()?; - let selected_files = (*widget).as_ref()?.content.get_selected(); + let selected_files = widget.as_ref()?.content.get_selected(); let file_names = selected_files.iter().map(|f| f.name.clone()).collect::>(); @@ -424,6 +524,9 @@ impl Widget for FileBrowser { fn get_core(&self) -> HResult<&WidgetCore> { Ok(&self.core) } + fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> { + Ok(&mut self.core) + } fn render_header(&self) -> HResult { let xsize = self.get_coordinates()?.xsize(); let file = self.selected_file()?; @@ -449,9 +552,9 @@ impl Widget for FileBrowser { let group = file.pretty_group().unwrap_or("NOGROUP".into()); let mtime = file.pretty_mtime().unwrap_or("NOMTIME".into()); - - let selection = (*self.main_widget().as_ref().unwrap().lock()?).as_ref()?.get_selection(); - let file_count = (*self.main_widget()?.lock()?).as_ref()?.content.len(); + let main_widget = self.main_widget()?.widget()?; + let selection = main_widget.lock()?.as_ref().unwrap().get_selection(); + let file_count = main_widget.lock()?.as_ref().unwrap().content.len(); let file_count = format!("{}", file_count); let digits = file_count.len(); let file_count = format!("{:digits$}/{:digits$}", @@ -466,22 +569,23 @@ impl Widget for FileBrowser { } fn refresh(&mut self) -> HResult<()> { //self.proc_view.lock()?.set_coordinates(self.get_coordinates()?); - self.handle_dir_events()?; + self.handle_dir_events().ok(); self.columns.refresh().ok(); - self.fix_left().ok(); - self.fix_selection().ok(); + self.set_left_selection().log(); + self.save_selection().log(); self.set_cwd().ok(); self.update_watches().ok(); self.update_preview().ok(); + self.columns.refresh().ok(); Ok(()) } fn get_drawlist(&self) -> HResult { - if self.columns.get_left_widget().is_err() { - Ok(self.columns.get_clearlist()? + &self.columns.get_drawlist()?) - } else { - Ok(self.columns.get_drawlist()?) - } + let left = self.left_widget()?.get_drawlist()?; + let main = self.main_widget()?.get_drawlist()?; + let prev = self.preview_widget()?.get_drawlist()?; + + Ok(left + &main + &prev) } fn on_key(&mut self, key: Key) -> HResult<()> { @@ -494,7 +598,7 @@ impl Widget for FileBrowser { self.proc_view.lock()?.popup()?; } , - _ => { self.columns.get_main_widget_mut()?.on_key(key)?; }, + _ => { self.main_widget_mut()?.on_key(key)?; }, } self.update_preview()?; Ok(()) diff --git a/src/files.rs b/src/files.rs index fe0dc8b..daabb9e 100644 --- a/src/files.rs +++ b/src/files.rs @@ -3,6 +3,7 @@ use std::ops::Index; use std::os::unix::fs::MetadataExt; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; +use std::hash::{Hash, Hasher}; use lscolors::LsColors; use mime_detective; @@ -294,6 +295,16 @@ impl PartialEq for File { } } +impl Hash for File { + fn hash(&self, state: &mut H) { + self.name.hash(state); + self.path.hash(state); + self.selected.hash(state); + } +} + +impl Eq for File {} + #[derive(Debug, Clone)] pub struct File { pub name: String, @@ -403,6 +414,16 @@ impl File { Some(mime.type_().as_str().to_string()) } + + pub fn parent(&self) -> Option { + Some(self.path.parent()?.to_path_buf()) + } + + pub fn parent_as_file(&self) -> HResult { + let pathbuf = self.parent()?; + File::new_from_path(&pathbuf) + } + pub fn grand_parent(&self) -> Option { Some(self.path.parent()?.parent()?.to_path_buf()) } diff --git a/src/hbox.rs b/src/hbox.rs index 6a2aa8e..7982333 100644 --- a/src/hbox.rs +++ b/src/hbox.rs @@ -2,12 +2,13 @@ use termion::event::{Event}; use crate::widget::{Widget, WidgetCore}; use crate::coordinates::{Coordinates, Size, Position}; -use crate::fail::{HResult, ErrorLog}; +use crate::fail::{HResult, HError, ErrorLog}; #[derive(PartialEq)] pub struct HBox { pub core: WidgetCore, pub widgets: Vec, + pub ratios: Option>, pub active: Option, } @@ -16,59 +17,89 @@ impl HBox where T: Widget + PartialEq { pub fn new(core: &WidgetCore) -> HBox { HBox { core: core.clone(), widgets: vec![], + ratios: None, active: None } } - pub fn resize_children(&mut self) { - let coords: Vec - = self.widgets.iter().map( - |w| - self.calculate_coordinates(w)).collect(); + pub fn resize_children(&mut self) -> HResult<()> { + let len = self.widgets.len(); + if len == 0 { return Ok(()) } + + let coords: Vec = self.calculate_coordinates()?; + + for (widget, coord) in self.widgets.iter_mut().zip(coords.iter()) { widget.set_coordinates(coord).log(); } + + Ok(()) } - pub fn push_widget(&mut self, widget: T) where T: PartialEq { + pub fn push_widget(&mut self, widget: T) { self.widgets.push(widget); - self.resize_children(); - self.refresh().log(); } pub fn pop_widget(&mut self) -> Option { let widget = self.widgets.pop(); - self.resize_children(); - self.refresh().log(); widget } pub fn prepend_widget(&mut self, widget: T) { self.widgets.insert(0, widget); - self.resize_children(); - self.refresh().log(); } - pub fn calculate_coordinates(&self, widget: &T) - -> Coordinates where T: PartialEq { - let coordinates = self.get_coordinates().unwrap(); - let xsize = coordinates.xsize(); - let ysize = coordinates.ysize(); - let top = coordinates.top().y(); + pub fn set_ratios(&mut self, ratios: Vec) { + self.ratios = Some(ratios); + } - let pos = self.widgets.iter().position(|w | w == widget).unwrap(); - let num = self.widgets.len(); + pub fn calculate_equal_ratios(&self) -> HResult> { + let len = self.widgets.len(); + if len == 0 { return HError::no_widget(); } - let widget_xsize = (xsize / num as u16) + 1; - let widget_xpos = widget_xsize * pos as u16; + let ratios = (0..len).map(|_| 100 / len).collect(); + Ok(ratios) + } - Coordinates { - size: Size((widget_xsize, - ysize)), - position: Position((widget_xpos, - top)) - } + pub fn calculate_coordinates(&self) + -> HResult> { + let box_coords = self.get_coordinates()?; + let box_xsize = box_coords.xsize(); + let box_ysize = box_coords.ysize(); + let box_top = box_coords.top().y(); + + let ratios = match &self.ratios { + Some(ratios) => ratios.clone(), + None => self.calculate_equal_ratios()? + }; + + let coords = ratios.iter().fold(Vec::::new(), |mut coords, ratio| { + let ratio = *ratio as u16; + let len = coords.len(); + let gap = if len == 0 { 0 } else { 1 }; + + let widget_xsize = box_xsize * ratio / 100; + let widget_xpos = if len == 0 { + box_coords.top().x() + } else { + let prev_coords = coords.last().unwrap(); + let prev_xsize = prev_coords.xsize(); + let prev_xpos = prev_coords.position().x(); + + prev_xsize + prev_xpos + gap + }; + + coords.push(Coordinates { + size: Size((widget_xsize, + box_ysize)), + position: Position((widget_xpos, + box_top)) + }); + coords + }); + + Ok(coords) } pub fn active_widget(&self) -> &T { @@ -92,7 +123,7 @@ impl Widget for HBox where T: Widget + PartialEq { } fn refresh(&mut self) -> HResult<()> { - self.resize_children(); + self.resize_children().log(); for child in &mut self.widgets { child.refresh()? } diff --git a/src/miller_columns.rs b/src/miller_columns.rs index 3a240cb..ca621e4 100644 --- a/src/miller_columns.rs +++ b/src/miller_columns.rs @@ -1,8 +1,7 @@ use termion::event::Key; use failure::Backtrace; -use crate::coordinates::{Coordinates, Position, Size}; -use crate::preview::Previewer; +use crate::coordinates::{Coordinates}; use crate::widget::{Widget, WidgetCore}; use crate::hbox::HBox; use crate::fail::{HError, HResult, ErrorLog}; @@ -11,11 +10,6 @@ use crate::fail::{HError, HResult, ErrorLog}; pub struct MillerColumns where T: Widget { pub widgets: HBox, pub core: WidgetCore, - // pub left: Option, - // pub main: Option, - //pub preview: AsyncPreviewer, - pub preview: Previewer, - pub ratio: (u16, u16, u16), } impl MillerColumns @@ -26,8 +20,6 @@ where MillerColumns { widgets: HBox::new(core), core: core.clone(), - ratio: (20, 30, 50), - preview: Previewer::new(core) } } @@ -46,64 +38,45 @@ where self.widgets.prepend_widget(widget); } - pub fn calculate_coordinates(&self) -> (Coordinates, Coordinates, Coordinates) { - let coordinates = self.get_coordinates().unwrap(); - let xsize = coordinates.xsize(); - let ysize = coordinates.ysize(); - let top = coordinates.top().y(); - let ratio = self.ratio; + pub fn set_ratios(&mut self, ratios: Vec) { + self.widgets.set_ratios(ratios); + } - let left_xsize = xsize * ratio.0 / 100; - let left_size = Size((left_xsize, ysize)); - let left_pos = 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 preview_xsize = xsize * ratio.2 / 100; - let preview_size = Size((preview_xsize - 1, ysize)); - let preview_pos = Position((left_xsize + main_xsize + 3, top)); - - let left_coords = Coordinates { - size: left_size, - position: left_pos, - }; - - let main_coords = Coordinates { - size: main_size, - position: main_pos, - }; - - let preview_coords = Coordinates { - size: preview_size, - position: preview_pos, - }; - - (left_coords, main_coords, preview_coords) + pub fn calculate_coordinates(&self) -> HResult> { + self.widgets.calculate_coordinates() } pub fn get_left_widget(&self) -> HResult<&T> { let len = self.widgets.widgets.len(); - if len < 2 { + if len < 3 { return Err(HError::NoWidgetError(Backtrace::new())); } - let widget = self.widgets.widgets.get(len - 2)?; + let widget = self.widgets.widgets.get(len - 3)?; Ok(widget) } pub fn get_left_widget_mut(&mut self) -> HResult<&mut T> { let len = self.widgets.widgets.len(); - if len < 2 { + if len < 3 { return Err(HError::NoWidgetError(Backtrace::new())); } - let widget = self.widgets.widgets.get_mut(len - 2)?; + let widget = self.widgets.widgets.get_mut(len - 3)?; Ok(widget) } pub fn get_main_widget(&self) -> HResult<&T> { - let widget = self.widgets.widgets.last()?; + let len = self.widgets.widgets.len(); + let widget = self.widgets.widgets.get(len-2)?; Ok(widget) } pub fn get_main_widget_mut(&mut self) -> HResult<&mut T> { + let len = self.widgets.widgets.len(); + let widget = self.widgets.widgets.get_mut(len-2)?; + Ok(widget) + } + pub fn get_right_widget(&self) -> HResult<&T> { + let widget = self.widgets.widgets.last()?; + Ok(widget) + } + pub fn get_right_widget_mut(&mut self) -> HResult<&mut T> { let widget = self.widgets.widgets.last_mut()?; Ok(widget) } @@ -121,29 +94,18 @@ where Ok(&mut self.core) } fn refresh(&mut self) -> HResult<()> { - let (left_coords, main_coords, preview_coords) = self.calculate_coordinates(); - - if let Ok(left_widget) = self.get_left_widget_mut() { - left_widget.set_coordinates(&left_coords).log(); - } - - if let Ok(main_widget) = self.get_main_widget_mut() { - main_widget.set_coordinates(&main_coords).log(); - } - - let preview_widget = &mut self.preview; - preview_widget.set_coordinates(&preview_coords)?; + self.widgets.refresh().log(); Ok(()) } fn get_drawlist(&self) -> HResult { let left_widget = self.get_left_widget()?; let main_widget = self.get_main_widget()?; - let preview = self.preview.get_drawlist()?; + let right_widget = self.get_right_widget()?; Ok(format!("{}{}{}", main_widget.get_drawlist()?, left_widget.get_drawlist()?, - preview)) + right_widget.get_drawlist()?)) } fn on_key(&mut self, key: Key) -> HResult<()> { diff --git a/src/minibuffer.rs b/src/minibuffer.rs index 690127f..33ccf3d 100644 --- a/src/minibuffer.rs +++ b/src/minibuffer.rs @@ -1,5 +1,3 @@ -use std::io::Write; - use termion::event::Key; use crate::coordinates::{Coordinates}; diff --git a/src/preview.rs b/src/preview.rs index c780588..872f1cd 100644 --- a/src/preview.rs +++ b/src/preview.rs @@ -11,6 +11,7 @@ use crate::fail::{HResult, HError, ErrorLog}; type HClosure = Box>) -> Result + Send>; +type HCClosure = Box>, WidgetCore) -> Result + Send>; type WidgetO = Box; lazy_static! { @@ -111,7 +112,7 @@ impl PartialEq for WillBeWidget { } } -pub struct WillBeWidget { +pub struct WillBeWidget { willbe: WillBe, core: WidgetCore } @@ -129,6 +130,24 @@ impl WillBeWidget { core: core.clone() } } + pub fn change_to(&mut self, closure: HCClosure) -> HResult<()> { + self.set_stale().log(); + + let sender = self.get_core()?.get_sender(); + let core = self.get_core()?.clone(); + + let mut willbe = WillBe::new_become(Box::new(move |stale| { + let core = core.clone(); + closure(stale, core) + })); + willbe.on_ready(Box::new(move |_| { + sender.send(crate::widget::Events::WidgetReady)?; + Ok(()) + }))?; + + self.willbe = willbe; + Ok(()) + } pub fn set_stale(&mut self) -> HResult<()> { self.willbe.set_stale() } @@ -136,6 +155,12 @@ impl WillBeWidget { self.willbe.check()?; Ok(self.willbe.thing.clone()) } + pub fn ready(&self) -> bool { + match self.willbe.check() { + Ok(_) => true, + _ => false + } + } } // impl WillBeWidget { @@ -157,11 +182,13 @@ impl Widget for WillBeWidget { Ok(&mut self.core) } fn refresh(&mut self) -> HResult<()> { - let widget = self.widget()?; - let mut widget = widget.lock()?; - let widget = widget.as_mut()?; - widget.set_coordinates(self.get_coordinates()?).log(); - widget.refresh() + if let Ok(widget) = self.widget() { + let mut widget = widget.lock()?; + let widget = widget.as_mut()?; + widget.set_coordinates(self.get_coordinates()?).log(); + widget.refresh().log(); + } + Ok(()) } fn get_drawlist(&self) -> HResult { if self.willbe.check().is_err() { @@ -170,6 +197,9 @@ impl Widget for WillBeWidget { let pos = crate::term::goto_xy(xpos, ypos); return Ok(clear + &pos + "...") } + if is_stale(&self.willbe.stale)? { + return self.get_clearlist() + } let widget = self.widget()?; let widget = widget.lock()?; let widget = widget.as_ref()?; @@ -199,7 +229,8 @@ impl PartialEq for Previewer { pub struct Previewer { widget: WillBeWidget>, core: WidgetCore, - file: Option + file: Option, + selection: Option } @@ -212,7 +243,8 @@ impl Previewer { })); Previewer { widget: willbe, core: core.clone(), - file: None} + file: None, + selection: None } } fn become_preview(&mut self, @@ -222,9 +254,10 @@ impl Previewer { self.widget.set_coordinates(&coordinates).ok(); } - pub fn set_file(&mut self, file: &File) { + pub fn set_file(&mut self, file: &File, selection: Option) { if Some(file) == self.file.as_ref() { return } self.file = Some(file.clone()); + self.selection = selection.clone(); let coordinates = self.get_coordinates().unwrap().clone(); let file = file.clone(); @@ -236,9 +269,13 @@ impl Previewer { kill_proc().unwrap(); let file = file.clone(); + let selection = selection.clone(); if file.kind == Kind::Directory { - let preview = Previewer::preview_dir(&file, &core, stale.clone()); + let preview = Previewer::preview_dir(&file, + selection, + &core, + stale.clone()); return preview; } @@ -261,7 +298,7 @@ impl Previewer { pub fn reload(&mut self) { if let Some(file) = self.file.clone() { self.file = None; - self.set_file(&file); + self.set_file(&file, self.selection.clone()); } } @@ -269,15 +306,21 @@ impl Previewer { Err(HError::PreviewFailed { file: file.name.clone() }) } - fn preview_dir(file: &File, core: &WidgetCore, stale: Arc>) + fn preview_dir(file: &File, + selection: Option, + core: &WidgetCore, + stale: Arc>) -> Result { let files = Files::new_from_path_cancellable(&file.path, stale.clone())?; let len = files.len(); - + if len == 0 || is_stale(&stale)? { return Previewer::preview_failed(&file) } let mut file_list = ListView::new(&core, files); + if let Some(selection) = selection { + file_list.select_file(&selection); + } file_list.set_coordinates(&core.coordinates)?; file_list.refresh()?; if is_stale(&stale)? { return Previewer::preview_failed(&file) } @@ -529,6 +572,9 @@ impl Widget for Box where T: Widget + ?Sized { fn get_core(&self) -> HResult<&WidgetCore> { Ok((**self).get_core()?) } + fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> { + Ok((**self).get_core_mut()?) + } fn render_header(&self) -> HResult { (**self).render_header() } diff --git a/src/proclist.rs b/src/proclist.rs index 79036b9..c758980 100644 --- a/src/proclist.rs +++ b/src/proclist.rs @@ -7,10 +7,10 @@ use std::io::{BufRead, BufReader}; use termion::event::Key; use unicode_width::UnicodeWidthStr; -use crate::coordinates::{Coordinates, Size, Position}; use crate::listview::{Listable, ListView}; use crate::textview::TextView; use crate::widget::{Widget, Events, WidgetCore}; +use crate::hbox::HBox; use crate::preview::WillBeWidget; use crate::fail::{HResult, HError, ErrorLog}; use crate::term; @@ -26,6 +26,12 @@ struct Process { } +impl PartialEq for Process { + fn eq(&self, other: &Process) -> bool { + self.cmd == other.cmd + } +} + impl Process { fn read_proc(&mut self) -> HResult<()> { let handle = self.handle.clone(); @@ -46,7 +52,8 @@ impl Process { sender.send(Events::WidgetReady).unwrap(); } Err(err) => { - dbg!(err); + let err: HResult<()> = Err(HError::from(err)); + err.log(); break; } } @@ -143,96 +150,117 @@ impl ListView> { } } +#[derive(PartialEq)] +enum ProcViewWidgets { + List(ListView>), + TextView(WillBeWidget), +} + +impl Widget for ProcViewWidgets { + fn get_core(&self) -> HResult<&WidgetCore> { + match self { + ProcViewWidgets::List(widget) => widget.get_core(), + ProcViewWidgets::TextView(widget) => widget.get_core() + } + } + fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> { + match self { + ProcViewWidgets::List(widget) => widget.get_core_mut(), + ProcViewWidgets::TextView(widget) => widget.get_core_mut() + } + } + fn refresh(&mut self) -> HResult<()> { + match self { + ProcViewWidgets::List(widget) => widget.refresh(), + ProcViewWidgets::TextView(widget) => widget.refresh() + } + } + fn get_drawlist(&self) -> HResult { + match self { + ProcViewWidgets::List(widget) => widget.get_drawlist(), + ProcViewWidgets::TextView(widget) => widget.get_drawlist() + } + } +} + pub struct ProcView { core: WidgetCore, - proc_list: ListView>, - textview: WillBeWidget, + hbox: HBox, viewing: Option } +impl HBox { + fn get_listview(&mut self) -> &mut ListView> { + match &mut self.widgets[0] { + ProcViewWidgets::List(listview) => listview, + _ => unreachable!() + } + } + fn get_textview(&mut self) -> &mut WillBeWidget { + match &mut self.widgets[1] { + ProcViewWidgets::TextView(textview) => textview, + _ => unreachable!() + } + } +} + impl ProcView { pub fn new(core: &WidgetCore) -> ProcView { let tcore = core.clone(); + let listview = ListView::new(&core, vec![]); let textview = Box::new(move |_| Ok(TextView::new_blank(&tcore))); + let textview = WillBeWidget::new(&core, textview); + let mut hbox = HBox::new(&core); + hbox.push_widget(ProcViewWidgets::List(listview)); + hbox.push_widget(ProcViewWidgets::TextView(textview)); + hbox.set_ratios(vec![33, 66]); + hbox.refresh().log(); ProcView { core: core.clone(), - proc_list: ListView::new(&core, vec![]), - textview: WillBeWidget::new(&core, textview), + hbox: hbox, viewing: None } } + fn get_listview(&mut self) -> &mut ListView> { + self.hbox.get_listview() + } + + fn get_textview(&mut self) -> &mut WillBeWidget { + self.hbox.get_textview() + } + pub fn run_proc(&mut self, cmd: &str) -> HResult<()> { - self.proc_list.run_proc(cmd)?; + self.get_listview().run_proc(cmd)?; Ok(()) } pub fn remove_proc(&mut self) -> HResult<()> { - let (_, coords) = self.calculate_coordinates(); - let coords2 = coords.clone(); - let mut core = self.core.clone(); - core.coordinates = coords; - - self.proc_list.remove_proc()?; - self.textview = WillBeWidget::new(&core.clone(), Box::new(move |_| { + self.get_listview().remove_proc()?; + self.get_textview().change_to(Box::new(move |_, core| { let mut textview = TextView::new_blank(&core); textview.refresh().log(); textview.animate_slide_up().log(); Ok(textview) - })); - self.textview.set_coordinates(&coords2).log(); + })).log(); Ok(()) } fn show_output(&mut self) -> HResult<()> { - if Some(self.proc_list.get_selection()) == self.viewing { + if Some(self.get_listview().get_selection()) == self.viewing { return Ok(()); } - let output = self.proc_list.selected_proc()?.output.lock()?.clone(); - let (_, coords) = self.calculate_coordinates(); - let mut core = self.core.clone(); - core.coordinates = coords; + let output = self.get_listview().selected_proc()?.output.lock()?.clone(); - self.textview = WillBeWidget::new(&core.clone(), Box::new(move |_| { + self.get_textview().change_to(Box::new(move |_, core| { let mut textview = TextView::new_blank(&core); textview.set_text(&output).log(); textview.animate_slide_up().log(); Ok(textview) - })); - self.viewing = Some(self.proc_list.get_selection()); + })).log(); + self.viewing = Some(self.get_listview().get_selection()); Ok(()) } - - pub fn calculate_coordinates(&self) -> (Coordinates, Coordinates) { - let coordinates = self.get_coordinates().unwrap(); - let xsize = coordinates.xsize(); - let ysize = coordinates.ysize(); - let top = coordinates.top().y(); - let ratio = (33, 66); - - let left_xsize = xsize * ratio.0 / 100; - let left_size = Size((left_xsize, ysize)); - let left_pos = 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 left_coords = Coordinates { - size: left_size, - position: left_pos, - }; - - let main_coords = Coordinates { - size: main_size, - position: main_pos, - }; - (left_coords, main_coords) - } - - } impl Widget for ProcView { @@ -243,29 +271,27 @@ impl Widget for ProcView { Ok(&mut self.core) } fn refresh(&mut self) -> HResult<()> { - let (lcoord, rcoord) = self.calculate_coordinates(); - self.proc_list.set_coordinates(&lcoord).log(); - self.textview.set_coordinates(&rcoord).log(); + self.hbox.refresh().log(); self.show_output().log(); - self.proc_list.refresh().log(); - self.textview.refresh().log(); + self.get_listview().refresh().log(); + self.get_textview().refresh().log(); Ok(()) } fn get_drawlist(&self) -> HResult { - Ok(self.proc_list.get_drawlist()? + &self.textview.get_drawlist()?) + self.hbox.get_drawlist() } fn on_key(&mut self, key: Key) -> HResult<()> { match key { Key::Char('w') => { return Err(HError::PopupFinnished) } Key::Char('d') => { self.remove_proc()? } - Key::Char('k') => { self.proc_list.kill_proc()? } + Key::Char('k') => { self.get_listview().kill_proc()? } Key::Up | Key::Char('p') => { - self.proc_list.move_up(); + self.get_listview().move_up(); } Key::Down | Key::Char('n') => { - self.proc_list.move_down(); + self.get_listview().move_down(); } _ => {} } diff --git a/src/tabview.rs b/src/tabview.rs index faa212f..77abdb4 100644 --- a/src/tabview.rs +++ b/src/tabview.rs @@ -80,6 +80,9 @@ impl Widget for TabView where T: Widget, TabView: Tabbable { fn get_core(&self) -> HResult<&WidgetCore> { Ok(&self.core) } + fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> { + Ok(&mut self.core) + } fn render_header(&self) -> HResult { let xsize = self.get_coordinates()?.xsize(); let header = self.active_tab_().render_header()?; diff --git a/src/widget.rs b/src/widget.rs index ba409cd..6dfac9b 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -4,7 +4,6 @@ use std::sync::mpsc::{Sender, Receiver, channel}; use termion::event::{Event, Key, MouseEvent}; use termion::input::TermRead; use termion::screen::AlternateScreen; -use failure::Backtrace; use crate::coordinates::{Coordinates, Position, Size}; @@ -81,15 +80,12 @@ impl WidgetCore { } pub trait Widget { - fn get_widget(&self) -> Box { - Box::new(crate::textview::TextView::new_blank(self.get_core().unwrap())) - } - fn get_core(&self) -> HResult<&WidgetCore> { - Err(HError::NoWidgetCoreError(Backtrace::new())) - } - fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> { - Err(HError::NoWidgetCoreError(Backtrace::new())) - } + fn get_core(&self) -> HResult<&WidgetCore>; // { + // Err(HError::NoWidgetCoreError(Backtrace::new())) + // } + fn get_core_mut(&mut self) -> HResult<&mut WidgetCore> ;// { + // Err(HError::NoWidgetCoreError(Backtrace::new())) + // } fn get_coordinates(&self) -> HResult<&Coordinates> { Ok(&self.get_core()?.coordinates) } @@ -205,6 +201,7 @@ pub trait Widget { fn popup(&mut self) -> HResult<()> { self.run_widget().log(); + self.clear().log(); self.get_core()?.get_sender().send(Events::ExclusiveEvent(None))?; Ok(()) } @@ -355,9 +352,9 @@ fn dispatch_events(rx: Receiver, tx: Sender) { _ => {} } if let Some(tx_event) = &tx_exclusive_event { - tx_event.send(event).unwrap(); + tx_event.send(event).ok(); } else { - tx.send(event).unwrap(); + tx.send(event).ok(); } } });