minibuffer Widgetified

This commit is contained in:
rabite 2019-02-25 18:23:48 +01:00
parent a6c147442c
commit 7fc77f8605
6 changed files with 274 additions and 109 deletions

View File

@ -172,9 +172,11 @@ impl FileBrowser {
if self.left_widget().is_err() { if self.left_widget().is_err() {
let file = self.selected_file()?.clone(); let file = self.selected_file()?.clone();
if let Some(grand_parent) = file.grand_parent() { 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 left_view = WillBeWidget::new(Box::new(move |_| {
let mut view let mut view
= ListView::new(Files::new_from_path(&grand_parent)?); = ListView::new(Files::new_from_path(&grand_parent)?);
view.set_coordinates(&coords);
Ok(view) Ok(view)
})); }));
self.columns.prepend_widget(left_view); self.columns.prepend_widget(left_view);
@ -232,18 +234,30 @@ impl FileBrowser {
let dir = self.minibuffer("cd: "); let dir = self.minibuffer("cd: ");
match dir { match dir {
Some(dir) => { Ok(dir) => {
self.columns.widgets.widgets.clear(); self.columns.widgets.widgets.clear();
let cwd = File::new_from_path(&std::path::PathBuf::from(&dir))?; let cwd = File::new_from_path(&std::path::PathBuf::from(&dir))?;
self.cwd = cwd; 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 middle = WillBeWidget::new(Box::new(move |_| {
let files = Files::new_from_path(&std::path::PathBuf::from(&dir))?; let files = Files::new_from_path(&dir.clone())?;
let listview = ListView::new(files); let mut listview = ListView::new(files);
listview.set_coordinates(&main_coords);
Ok(listview) 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); self.columns.push_widget(middle);
}, },
None => {} Err(_) => {}
} }
Ok(()) Ok(())
} }

View File

@ -342,8 +342,10 @@ impl ListView<Files>
let file_names let file_names
= selected_files.iter().map(|f| f.name.clone()).collect::<Vec<String>>(); = selected_files.iter().map(|f| f.name.clone()).collect::<Vec<String>>();
match self.minibuffer("exec ($s for selected file(s))") { let cmd = self.minibuffer("exec:");
Some(cmd) => {
match cmd {
Ok(cmd) => {
self.show_status(&format!("Running: \"{}\"", &cmd)); self.show_status(&format!("Running: \"{}\"", &cmd));
let filename = self.selected_file().name.clone(); let filename = self.selected_file().name.clone();
@ -375,7 +377,7 @@ impl ListView<Files>
cmd, err)), cmd, err)),
} }
} }
None => self.show_status(""), Err(_) => self.show_status(""),
} }
} }

View File

@ -39,6 +39,8 @@ mod hbox;
mod tabview; mod tabview;
mod async_widget; mod async_widget;
mod fail; mod fail;
mod minibuffer;
use window::Window; use window::Window;

211
src/minibuffer.rs Normal file
View File

@ -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;
}
_ => {}
}
}
}

View File

@ -1,6 +1,7 @@
use termion::event::{Event, Key, MouseEvent}; use termion::event::{Event, Key, MouseEvent};
use crate::coordinates::{Coordinates, Position, Size}; use crate::coordinates::{Coordinates, Position, Size};
use crate::fail::HResult;
use std::io::{BufWriter, Write}; use std::io::{BufWriter, Write};
@ -48,7 +49,7 @@ pub trait Widget {
crate::window::show_status(status); crate::window::show_status(status);
} }
fn minibuffer(&self, query: &str) -> Option<String> { fn minibuffer(&self, query: &str) -> HResult<String> {
crate::window::minibuffer(query) crate::window::minibuffer(query)
} }
@ -115,6 +116,15 @@ pub trait Widget {
.collect() .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) { fn animate_slide_up(&mut self) {
let coords = self.get_coordinates().clone(); let coords = self.get_coordinates().clone();
let xpos = coords.position().x(); let xpos = coords.position().x();

View File

@ -11,17 +11,20 @@ use crate::term::ScreenExt;
use crate::coordinates::{Coordinates, Position, Size}; use crate::coordinates::{Coordinates, Position, Size};
use crate::widget::Widget; use crate::widget::Widget;
use crate::minibuffer::MiniBuffer;
use crate::fail::HResult; use crate::fail::HResult;
lazy_static! { lazy_static! {
static ref TX_EVENT: Arc<Mutex<Option<Sender<Events>>>> = { Arc::new(Mutex::new(None)) }; 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 { pub enum Events {
InputEvent(Event), InputEvent(Event),
WidgetReady WidgetReady,
ExclusiveInput(bool),
} }
pub struct Window<T> pub struct Window<T>
@ -87,19 +90,28 @@ where
let (tx_event_internal, rx_event_internal) = channel(); let (tx_event_internal, rx_event_internal) = channel();
let (tx_event, rx_event) = channel(); let (tx_event, rx_event) = channel();
*TX_EVENT.try_lock().unwrap() = Some(tx_event); *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()); 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() { for event in rx_event_internal.iter() {
//Self::clear_status(); //Self::clear_status();
//let event = event.unwrap(); //let event = event.unwrap();
dbg!(&event);
match event { match event {
Events::InputEvent(event) => { Events::InputEvent(event) => {
self.widget.on_event(event); self.widget.on_event(event);
self.screen.cursor_hide(); self.screen.cursor_hide();
self.draw(); self.draw();
if !exclusive_mode {
tx_request_input.send(()).unwrap();
}
},
Events::ExclusiveInput(setting) => {
exclusive_mode = setting
} }
_ => { _ => {
self.widget.refresh(); 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 || { std::thread::spawn(move || {
for event in rx.iter() { for event in rx.iter() {
dbg!(&event);
tx.send(event).unwrap(); tx.send(event).unwrap();
} }
}); });
} }
fn input_thread(tx: Sender<Events>) { fn input_thread(tx: Sender<Events>, request_input: Receiver<()>) {
std::thread::spawn(move || { std::thread::spawn(move || {
for input in stdin().events() { for _ in request_input.iter() {
let input = input.unwrap(); for input in stdin().events() {
tx.send(Events::InputEvent(input)).unwrap(); let input = input.unwrap();
tx.send(Events::InputEvent(input)).unwrap();
break;
}
} }
}); });
} }
@ -188,97 +203,8 @@ pub fn show_status(status: &str) {
draw_status(); draw_status();
} }
pub fn minibuffer(query: &str) -> Option<String> { pub fn minibuffer(query: &str) -> HResult<String> {
show_status(&(query.to_string() + ": ")); MINIBUFFER.lock()?.query(query)
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 find_bins(comp_name: &str) -> Vec<String> { pub fn find_bins(comp_name: &str) -> Vec<String> {