summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/file_browser.rs22
-rw-r--r--src/listview.rs8
-rw-r--r--src/main.rs2
-rw-r--r--src/minibuffer.rs211
-rw-r--r--src/widget.rs12
-rw-r--r--src/window.rs128
6 files changed, 274 insertions, 109 deletions
diff --git a/src/file_browser.rs b/src/file_browser.rs
index 910305e..8ede380 100644
--- a/src/file_browser.rs
+++ b/src/file_browser.rs
@@ -172,9 +172,11 @@ impl FileBrowser {
if self.left_widget().is_err() {
let file = self.selected_file()?.clone();
if let Some(grand_parent) = file.grand_parent() {
+ let (coords, _, _) = self.columns.calculate_coordinates();
let mut left_view = WillBeWidget::new(Box::new(move |_| {
let mut view
= ListView::new(Files::new_from_path(&grand_parent)?);
+ view.set_coordinates(&coords);
Ok(view)
}));
self.columns.prepend_widget(left_view);
@@ -232,18 +234,30 @@ impl FileBrowser {
let dir = self.minibuffer("cd: ");
match dir {
- Some(dir) => {
+ Ok(dir) => {
self.columns.widgets.widgets.clear();
let cwd = File::new_from_path(&std::path::PathBuf::from(&dir))?;
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 middle = WillBeWidget::new(Box::new(move |_| {
- let files = Files::new_from_path(&std::path::PathBuf::from(&dir))?;
- let listview = ListView::new(files);
+ let files = Files::new_from_path(&dir.clone())?;
+ let mut listview = ListView::new(files);
+ listview.set_coordinates(&main_coords);
+ Ok(listview)
+ }));
+ let left = WillBeWidget::new(Box::new(move |_| {
+ let files = Files::new_from_path(&left_dir.parent()?)?;
+ let mut listview = ListView::new(files);
+ listview.set_coordinates(&left_coords);
Ok(listview)
}));
+ self.columns.push_widget(left);
self.columns.push_widget(middle);
},
- None => {}
+ Err(_) => {}
}
Ok(())
}
diff --git a/src/listview.rs b/src/listview.rs
index a64a340..904baa9 100644
--- a/src/listview.rs
+++ b/src/listview.rs
@@ -342,8 +342,10 @@ impl ListView<Files>
let file_names
= selected_files.iter().map(|f| f.name.clone()).collect::<Vec<String>>();
- match self.minibuffer("exec ($s for selected file(s))") {
- Some(cmd) => {
+ let cmd = self.minibuffer("exec:");
+
+ match cmd {
+ Ok(cmd) => {
self.show_status(&format!("Running: \"{}\"", &cmd));
let filename = self.selected_file().name.clone();
@@ -375,7 +377,7 @@ impl ListView<Files>
cmd, err)),
}
}
- None => self.show_status(""),
+ Err(_) => self.show_status(""),
}
}
diff --git a/src/main.rs b/src/main.rs
index 3fd895c..2e1cfb1 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -39,6 +39,8 @@ mod hbox;
mod tabview;
mod async_widget;
mod fail;
+mod minibuffer;
+
use window::Window;
diff --git a/src/minibuffer.rs b/src/minibuffer.rs
new file mode 100644
index 0000000..8c45bd0
--- /dev/null
+++ b/src/minibuffer.rs
@@ -0,0 +1,211 @@
+use termion::event::Key;
+use termion::input::TermRead;
+
+use std::io::{stdin, stdout, Write};
+
+use crate::coordinates::{Coordinates};
+use crate::widget::Widget;
+use crate::window::{send_event, Events};
+use crate::fail::HResult;
+
+pub struct MiniBuffer {
+ coordinates: Coordinates,
+ query: String,
+ input: String,
+ done: bool,
+ position: usize,
+ history: Vec<String>
+}
+
+impl MiniBuffer {
+ pub fn new() -> MiniBuffer {
+ let xsize = crate::term::xsize();
+ let ysize = crate::term::ysize();
+ let coordinates = Coordinates::new_at(xsize, 1, 1, ysize);
+ MiniBuffer {
+ coordinates: coordinates,
+ query: String::new(),
+ input: String::new(),
+ done: false,
+ position: 0,
+ history: vec![]
+ }
+ }
+
+ pub fn query(&mut self, query: &str) -> HResult<String> {
+ self.query = query.to_string();
+ self.input.clear();
+ self.done = false;
+ self.position = 0;
+
+ send_event(Events::ExclusiveInput(true))?;
+
+ self.draw()?;
+ write!(stdout(), "{}{}",
+ termion::cursor::Show,
+ termion::cursor::Save)?;
+ stdout().flush()?;
+
+
+ for event in stdin().events() {
+ let event = event?;
+ self.on_event(event);
+ if self.done {
+ break
+ }
+ self.draw()?;
+
+ write!(stdout(), "{}", termion::cursor::Restore)?;
+ if self.position != 0 {
+ write!(stdout(),
+ "{}",
+ termion::cursor::Right(self.position as u16))?;
+ }
+ stdout().flush()?;
+ }
+
+ self.done = false;
+
+ send_event(Events::ExclusiveInput(false))?;
+
+ Ok(self.input.clone())
+ }
+}
+
+pub fn find_bins(comp_name: &str) -> Vec<String> {
+ let paths = std::env::var_os("PATH").unwrap()
+ .to_string_lossy()
+ .split(":")
+ .map(|s| s.to_string())
+ .collect::<Vec<String>>();
+
+ paths.iter().map(|path| {
+ std::fs::read_dir(path).unwrap().flat_map(|file| {
+ let file = file.unwrap();
+ let name = file.file_name().into_string().unwrap();
+ if name.starts_with(comp_name) {
+ Some(name)
+ } else {
+ None
+ }
+ }).collect::<Vec<String>>()
+ }).flatten().collect::<Vec<String>>()
+}
+
+pub fn find_files(mut comp_name: String) -> Vec<String> {
+ let mut path = std::path::PathBuf::from(&comp_name);
+
+ let dir = if comp_name.starts_with("/") {
+ comp_name = path.file_name().unwrap().to_string_lossy().to_string();
+ path.pop();
+ path.to_string_lossy().to_string()
+ } else {
+ std::env::current_dir().unwrap().to_string_lossy().to_string()
+ };
+
+ let reader = std::fs::read_dir(dir.clone());
+ if reader.is_err() { return vec![] }
+ let reader = reader.unwrap();
+
+ reader.flat_map(|file| {
+ let file = file.unwrap();
+ let name = file.file_name().into_string().unwrap();
+ if name.starts_with(&comp_name) {
+ if file.file_type().unwrap().is_dir() {
+ Some(format!("{}/{}/", &dir, name))
+ } else {
+ Some(format!("/{}/", name))
+ }
+ } else {
+ None
+ }
+ }).collect::<Vec<String>>()
+}
+
+impl Widget for MiniBuffer {
+ fn get_coordinates(&self) -> &Coordinates {
+ &self.coordinates
+ }
+ fn set_coordinates(&mut self, coordinates: &Coordinates) {
+ self.coordinates = coordinates.clone();
+ self.refresh();
+ }
+ fn render_header(&self) -> String {
+ "".to_string()
+ }
+ fn refresh(&mut self) {
+ }
+
+ fn get_drawlist(&self) -> String {
+ let (xpos, ypos) = self.get_coordinates().u16position();
+ format!("{}{}{}: {}",
+ crate::term::goto_xy(xpos, ypos),
+ termion::clear::CurrentLine,
+ self.query,
+ self.input)
+ }
+
+ fn on_key(&mut self, key: Key) {
+ match key {
+ Key::Esc | Key::Ctrl('c') => { self.input.clear(); self.done = true; },
+ Key::Char('\n') => {
+ if self.input != "" {
+ self.history.push(self.input.clone());
+ }
+ self.done = true;
+ }
+ Key::Char('\t') => {
+ if !self.input.ends_with(" ") {
+ let part = self.input.rsplitn(2, " ").take(1)
+ .map(|s| s.to_string()).collect::<String>();
+ let completions = find_files(part.clone());
+ if !completions.is_empty() {
+ self.input
+ = self.input[..self.input.len() - part.len()].to_string();
+ self.input.push_str(&completions[0]);
+ self.position += &completions[0].len() - part.len();
+ } else {
+ let completions = find_bins(&part);
+ if !completions.is_empty() {
+ self.input = self.input[..self.input.len()
+ - part.len()].to_string();
+ self.input.push_str(&completions[0]);
+ self.position += &completions[0].len() - part.len();
+ }
+ }
+ } else {
+ self.input += "$s";
+ self.position += 2
+ }
+ }
+ Key::Backspace => {
+ if self.position != 0 {
+ self.input.remove(self.position - 1);
+ self.position -= 1;
+ }
+ }
+ Key::Delete | Key::Ctrl('d') => {
+ if self.position != self.input.len() {
+ self.input.remove(self.position);
+ }
+ }
+ Key::Left | Key::Ctrl('b') => {
+ if self.position != 0 {
+ self.position -= 1;
+ }
+ }
+ Key::Right | Key::Ctrl('f') => {
+ if self.position != self.input.len() {
+ self.position += 1;
+ }
+ }
+ Key::Ctrl('a') => { self.position = 0 },
+ Key::Ctrl('e') => { self.position = self.input.len(); },
+ Key::Char(key) => {
+ self.input.insert(self.position, key);
+ self.position += 1;
+ }
+ _ => {}
+ }
+ }
+}
diff --git a/src/widget.rs b/src/widget.rs
index ccf8f72..d41900f 100644
--- a/src/widget.rs
+++ b/src/widget.rs
@@ -1,6 +1,7 @@
use termion::event::{Event, Key, MouseEvent};
use crate::coordinates::{Coordinates, Position, Size};
+use crate::fail::HResult;
use std::io::{BufWriter, Write};
@@ -48,7 +49,7 @@ pub trait Widget {
crate::window::show_status(status);
}
- fn minibuffer(&self, query: &str) -> Option<String> {
+ fn minibuffer(&self, query: &str) -> HResult<String> {
crate::window::minibuffer(query)
}
@@ -115,6 +116,15 @@ pub trait Widget {
.collect()
}
+ fn draw(&self) -> HResult<()> {
+ let drawlist = self.get_drawlist();
+ let mut bufout = BufWriter::new(std::io::stdout());
+
+ write!(bufout, "{}", drawlist)?;
+ bufout.flush()?;
+ Ok(())
+ }
+
fn animate_slide_up(&mut self) {
let coords = self.get_coordinates().clone();
let xpos = coords.position().x();
diff --git a/src/window.rs b/src/window.rs
index b31058d..fc08b47 100644
--- a/src/window.rs
+++ b/src/window.rs
@@ -11,17 +11,20 @@ use crate::term::ScreenExt;
use crate::coordinates::{Coordinates, Position, Size};
use crate::widget::Widget;
+use crate::minibuffer::MiniBuffer;
use crate::fail::HResult;
lazy_static! {
static ref TX_EVENT: Arc<Mutex<Option<Sender<Events>>>> = { Arc::new(Mutex::new(None)) };
+ static ref MINIBUFFER: Arc<Mutex<MiniBuffer>>
+ = Arc::new(Mutex::new(MiniBuffer::new()));
}
-#[derive(Debug)]
pub enum Events {
InputEvent(Event),
- WidgetReady
+ WidgetReady,
+ ExclusiveInput(bool),
}
pub struct Window<T>
@@ -87,19 +90,28 @@ where
let (tx_event_internal, rx_event_internal) = channel();
let (tx_event, rx_event) = channel();
*TX_EVENT.try_lock().unwrap() = Some(tx_event);
+ let (tx_request_input, rx_request_input) = channel();
+
+ let mut exclusive_mode = false;
event_thread(rx_event, tx_event_internal.clone());
- input_thread(tx_event_internal);
+ input_thread(tx_event_internal.clone(), rx_request_input);
+ tx_request_input.send(()).unwrap();
for event in rx_event_internal.iter() {
//Self::clear_status();
//let event = event.unwrap();
- dbg!(&event);
match event {
Events::InputEvent(event) => {
self.widget.on_event(event);
self.screen.cursor_hide();
self.draw();
+ if !exclusive_mode {
+ tx_request_input.send(()).unwrap();
+ }
+ },
+ Events::ExclusiveInput(setting) => {
+ exclusive_mode = setting
}
_ => {
self.widget.refresh();
@@ -110,20 +122,23 @@ where
}
}
-fn event_thread(rx: Receiver<Events>, tx: Sender<Events>) {
+fn event_thread(rx: Receiver<Events>,
+ tx: Sender<Events>) {
std::thread::spawn(move || {
for event in rx.iter() {
- dbg!(&event);
tx.send(event).unwrap();
}
});
}
-fn input_thread(tx: Sender<Events>) {
+fn input_thread(tx: Sender<Events>, request_input: Receiver<()>) {
std::thread::spawn(move || {
- for input in stdin().events() {
- let input = input.unwrap();
- tx.send(Events::InputEvent(input)).unwrap();
+ for _ in request_input.iter() {
+ for input in stdin().events() {
+ let input = input.unwrap();
+ tx.send(Events::InputEvent(input)).unwrap();
+ break;
+ }
}
});
}
@@ -188,97 +203,8 @@ pub fn show_status(status: &str) {
draw_status();
}
-pub fn minibuffer(query: &str) -> Option<String> {
- show_status(&(query.to_string() + ": "));
- write!(stdout(), "{}{}",
- termion::cursor::Show,
- termion::cursor::Save).unwrap();
- stdout().flush().unwrap();
-
- let mut buffer = "".to_string();
- let mut pos = 0;
-
- for key in stdin().events() {
-
- match key {
- Ok(Event::Key(key)) => match key {
- Key::Esc | Key::Ctrl('c') => break,
- Key::Char('\n') => {
- if buffer == "" {
- write!(stdout(), "{}", termion::cursor::Hide).unwrap();
- stdout().flush().unwrap();
- return None;
- } else {
- write!(stdout(), "{}", termion::cursor::Hide).unwrap();
- stdout().flush().unwrap();
- return Some(buffer);
- }
- }
- Key::Char('\t') => {
- if !buffer.ends_with(" ") {
- let part = buffer.rsplitn(2, " ").take(1)
- .map(|s| s.to_string()).collect::<String>();
- let completions = find_files(part.clone());
- if !completions.is_empty() {
- buffer = buffer[..buffer.len() - part.len()].to_string();
- buffer.push_str(&completions[0]);
- pos += &completions[0].len() - part.len();
- } else {
- let completions = find_bins(&part);
- if !completions.is_empty() {
- buffer = buffer[..buffer.len() - part.len()].to_string();
- buffer.push_str(&completions[0]);
- pos += &completions[0].len() - part.len();
- }
- }
- } else {
- buffer += "$s";
- pos += 2
- }
- }
- Key::Backspace => {
- if pos != 0 {
- buffer.remove(pos - 1);
- pos -= 1;
- }
- }
- Key::Delete | Key::Ctrl('d') => {
- if pos != buffer.len() {
- buffer.remove(pos);
- }
- }
- Key::Left | Key::Ctrl('b') => {
- if pos != 0 {
- pos -= 1;
- }
- }
- Key::Right | Key::Ctrl('f') => {
- if pos != buffer.len() {
- pos += 1;
- }
- }
- Key::Ctrl('a') => { pos = 0 },
- Key::Ctrl('e') => { pos = buffer.len(); },
- Key::Char(key) => {
- buffer.insert(pos, key);
- pos += 1;
- }
- _ => {}
- },
- _ => {}
- }
- show_status(&(query.to_string() + ": " + &buffer));
-
- write!(stdout(), "{}", termion::cursor::Restore).unwrap();
- stdout().flush().unwrap();
- if pos != 0 {
- write!(stdout(),
- "{}",
- format!("{}", termion::cursor::Right(pos as u16))).unwrap();
- }
- stdout().flush().unwrap();
- }
- None
+pub fn minibuffer(query: &str) -> HResult<String> {
+ MINIBUFFER.lock()?.query(query)
}
pub fn find_bins(comp_name: &str) -> Vec<String> {