summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJiayi Zhao <jeff.no.zhao@gmail.com>2020-02-22 19:54:41 -0500
committerJiayi Zhao <jeff.no.zhao@gmail.com>2020-02-22 19:54:41 -0500
commitd38dcdbbee44187bdb605dbf9bbf9c6c6d3e4f35 (patch)
tree91b059b99793fe250c67f66be3930a298117e0b3 /src
parent03594099dafb4cda04e50f087df61cf76e2034d0 (diff)
implement textfield widget
- for asking users for long input strings - implement prompt widget - for prompting users for a single key response
Diffstat (limited to 'src')
-rw-r--r--src/commands/command_line.rs7
-rw-r--r--src/commands/delete_files.rs91
-rw-r--r--src/commands/rename_file.rs4
-rw-r--r--src/commands/set_mode.rs2
-rw-r--r--src/context.rs6
-rw-r--r--src/fs/dirlist.rs4
-rw-r--r--src/fs/entry.rs16
-rw-r--r--src/fs/metadata.rs6
-rw-r--r--src/main.rs2
-rw-r--r--src/run.rs56
-rw-r--r--src/ui/widgets/mod.rs2
-rw-r--r--src/ui/widgets/tui_prompt.rs60
-rw-r--r--src/ui/widgets/tui_textfield.rs176
-rw-r--r--src/ui/widgets/tui_view.rs40
-rw-r--r--src/util/format.rs2
-rw-r--r--src/util/mod.rs1
-rw-r--r--src/util/unix.rs (renamed from src/unix.rs)5
17 files changed, 355 insertions, 125 deletions
diff --git a/src/commands/command_line.rs b/src/commands/command_line.rs
index a00f442..f44c4a5 100644
--- a/src/commands/command_line.rs
+++ b/src/commands/command_line.rs
@@ -23,8 +23,11 @@ impl CommandLine {
context: &mut JoshutoContext,
backend: &mut TuiBackend,
) -> JoshutoResult<()> {
- // let mut textfield = TuiTextField::new(backend, &context.events);
- let user_input: Option<String> = None; // textfield.readline();
+ let mut textfield = TuiTextField::default()
+ .prompt(":")
+ .prefix(self.prefix.as_str())
+ .suffix(self.suffix.as_str());
+ let user_input: Option<String> = textfield.get_input(backend, &context);
if let Some(s) = user_input {
let trimmed = s.trim_start();
diff --git a/src/commands/delete_files.rs b/src/commands/delete_files.rs
index 2219283..78344d1 100644
--- a/src/commands/delete_files.rs
+++ b/src/commands/delete_files.rs
@@ -2,8 +2,7 @@ use std::fs;
use std::io::{self, Write};
use std::path;
-use termion::clear;
-use termion::cursor::Goto;
+use termion::event::Key;
use crate::commands::{JoshutoCommand, JoshutoRunnable, ReloadDirList};
use crate::context::JoshutoContext;
@@ -11,6 +10,8 @@ use crate::error::JoshutoResult;
use crate::ui::TuiBackend;
use crate::util::event::Event;
+use crate::ui::widgets::TuiPrompt;
+
#[derive(Clone, Debug)]
pub struct DeleteFiles;
@@ -36,84 +37,46 @@ impl DeleteFiles {
}
fn delete_files(context: &mut JoshutoContext, backend: &mut TuiBackend) -> std::io::Result<()> {
- let curr_tab = &mut context.tabs[context.curr_tab_index];
+ let curr_tab = &context.tabs[context.curr_tab_index];
let paths = match curr_tab.curr_list_ref() {
Some(s) => s.get_selected_paths(),
None => Vec::new(),
};
+ let paths_len = paths.len();
- if paths.is_empty() {
+ if paths_len == 0 {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
"no files selected",
));
}
- let frame = backend.terminal.get_frame();
- let f_size = frame.size();
-
- let termion_terminal = backend.terminal.backend_mut();
-
- write!(
- termion_terminal,
- "{}Delete {} files? (y/N){}",
- Goto(1, f_size.height),
- paths.len(),
- clear::AfterCursor
- );
-
- io::stdout().flush().ok();
-
- let mut ch = termion::event::Key::Char('n');
- while let Ok(evt) = context.events.next() {
- match evt {
- Event::Input(key) => {
- if key == termion::event::Key::Char('y')
- || key == termion::event::Key::Char('\n')
- {
- ch = termion::event::Key::Char('y');
- }
- break;
- }
- _ => {}
- }
- }
-
- if ch == termion::event::Key::Char('y') {
- if paths.len() > 1 {
- write!(
- termion_terminal,
- "{}Are you sure? (Y/n){}",
- Goto(1, f_size.height),
- clear::AfterCursor
- );
-
- io::stdout().flush().ok();
+ let ch = {
+ let prompt_str = format!("Delete {} files? (y/N)", paths_len);
+ let mut prompt = TuiPrompt::new(&prompt_str);
+ prompt.get_key(backend, &context)
+ };
- while let Ok(evt) = context.events.next() {
- match evt {
- Event::Input(key) => {
- ch = key;
- break;
- }
- _ => {}
- }
+ if ch == Key::Char('y') {
+ if paths_len > 1 {
+ let ch = {
+ let prompt_str = "Are you sure? (Y/n)";
+ let mut prompt = TuiPrompt::new(prompt_str);
+ prompt.get_key(backend, &context)
+ };
+ if ch == Key::Char('y') || ch == Key::Char('\n') {
+ Self::remove_files(&paths)?;
+ ReloadDirList::reload(context.curr_tab_index, context)?;
+ let msg = format!("Deleted {} files", paths_len);
+ context.message_queue.push_back(msg);
}
} else {
- ch = termion::event::Key::Char('y');
+ Self::remove_files(&paths)?;
+ ReloadDirList::reload(context.curr_tab_index, context)?;
+ let msg = format!("Deleted {} files", paths_len);
+ context.message_queue.push_back(msg);
}
}
-
- if ch == termion::event::Key::Char('y') {
- Self::remove_files(&paths)?;
- ReloadDirList::reload(context.curr_tab_index, context)?;
- }
- write!(
- termion_terminal,
- "{}{}",
- Goto(1, f_size.height),
- clear::AfterCursor
- );
Ok(())
}
}
diff --git a/src/commands/rename_file.rs b/src/commands/rename_file.rs
index dad9632..763a638 100644
--- a/src/commands/rename_file.rs
+++ b/src/commands/rename_file.rs
@@ -84,8 +84,8 @@ impl RenameFileAppend {
let prefix;
let suffix;
if let Some(ext) = file_name.rfind('.') {
- prefix = format!("rename {}", &file_name[0..ext]);
- suffix = String::from(&file_name[ext..]);
+ prefix = format!("rename {}", &file_name[0..ext + 1]);
+ suffix = String::from(&file_name[ext + 1..]);
} else {
prefix = format!("rename {}", file_name);
suffix = String::new();
diff --git a/src/commands/set_mode.rs b/src/commands/set_mode.rs
index 8a974e9..94a6d23 100644
--- a/src/commands/set_mode.rs
+++ b/src/commands/set_mode.rs
@@ -4,7 +4,7 @@ use crate::error::JoshutoResult;
use crate::fs::JoshutoDirEntry;
use crate::ui::widgets::TuiTextField;
use crate::ui::TuiBackend;
-use crate::unix;
+use crate::util::unix;
#[derive(Clone, Debug)]
pub struct SetMode;
diff --git a/src/context.rs b/src/context.rs
index c0651d9..328f9f6 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -6,7 +6,7 @@ use crate::io::IOWorkerThread;
use crate::tab::JoshutoTab;
use crate::util::event::Events;
-pub const MESSAGE_VISIBLE_DURATION: usize = 2;
+pub const MESSAGE_VISIBLE_DURATION: usize = 1;
pub struct JoshutoContext {
pub exit: bool,
@@ -15,6 +15,7 @@ pub struct JoshutoContext {
pub worker_queue: VecDeque<IOWorkerThread>,
pub trx: (mpsc::SyncSender<u64>, mpsc::Receiver<u64>),
+ pub worker_msg: Option<String>,
pub message_queue: VecDeque<String>,
pub message_elapse: usize,
pub events: Events,
@@ -30,8 +31,9 @@ impl JoshutoContext {
tabs: Vec::new(),
worker_queue: VecDeque::with_capacity(10),
trx: mpsc::sync_channel::<u64>(1),
+ worker_msg: None,
message_queue: VecDeque::with_capacity(4),
- message_elapse: MESSAGE_VISIBLE_DURATION,
+ message_elapse: 0,
events: Events::new(),
config_t,
diff --git a/src/fs/dirlist.rs b/src/fs/dirlist.rs
index 09c752c..cf53457 100644
--- a/src/fs/dirlist.rs
+++ b/src/fs/dirlist.rs
@@ -2,7 +2,6 @@ use std::{fs, path};
use crate::fs::{JoshutoDirEntry, JoshutoMetadata};
use crate::sort::SortOption;
-use crate::window::JoshutoPageState;
#[derive(Debug)]
pub struct JoshutoDirList {
@@ -12,7 +11,6 @@ pub struct JoshutoDirList {
order_outdated: bool,
pub metadata: JoshutoMetadata,
pub contents: Vec<JoshutoDirEntry>,
- pub pagestate: JoshutoPageState,
}
impl JoshutoDirList {
@@ -25,7 +23,6 @@ impl JoshutoDirList {
let index = if contents.is_empty() { None } else { Some(0) };
let metadata = JoshutoMetadata::from(&path)?;
- let pagestate = JoshutoPageState::default();
Ok(JoshutoDirList {
index,
@@ -34,7 +31,6 @@ impl JoshutoDirList {
order_outdated: false,
metadata,
contents,
- pagestate,
})
}
diff --git a/src/fs/entry.rs b/src/fs/entry.rs
index 5cb8a2b..84e818c 100644
--- a/src/fs/entry.rs
+++ b/src/fs/entry.rs
@@ -4,6 +4,7 @@ use tui::style::{Color, Modifier, Style};
use crate::fs::JoshutoMetadata;
+use crate::util::unix;
use crate::THEME_T;
#[derive(Clone, Debug)]
@@ -117,6 +118,8 @@ impl JoshutoDirEntry {
let metadata = &self.metadata;
let filetype = metadata.file_type;
+ use std::os::unix::fs::MetadataExt;
+
let mut modifier = Modifier::empty();
if filetype.is_dir() {
@@ -156,6 +159,8 @@ impl JoshutoDirEntry {
}
pub fn get_style(&self) -> Style {
+ use std::os::unix::fs::MetadataExt;
+
let metadata = &self.metadata;
let filetype = metadata.file_type;
@@ -194,6 +199,17 @@ impl JoshutoDirEntry {
style = style.fg(THEME_T.link.fg).bg(THEME_T.link.bg);
style = style.modifier(modifier);
+ } else if unix::is_executable(metadata.mode) {
+ let mut modifier = Modifier::empty();
+ if THEME_T.link.bold {
+ modifier.insert(Modifier::BOLD);
+ }
+ if THEME_T.link.underline {
+ modifier.insert(Modifier::UNDERLINED);
+ }
+
+ style = style.fg(THEME_T.executable.fg).bg(THEME_T.executable.bg);
+ style = style.modifier(modifier);
} else {
match self.file_path().extension() {
None => {}
diff --git a/src/fs/metadata.rs b/src/fs/metadata.rs
index f3fea53..dbf4d37 100644
--- a/src/fs/metadata.rs
+++ b/src/fs/metadata.rs
@@ -10,6 +10,8 @@ pub struct JoshutoMetadata {
pub uid: u32,
#[cfg(unix)]
pub gid: u32,
+ #[cfg(unix)]
+ pub mode: u32,
}
impl JoshutoMetadata {
@@ -28,6 +30,8 @@ impl JoshutoMetadata {
let uid = metadata.uid();
#[cfg(unix)]
let gid = metadata.gid();
+ #[cfg(unix)]
+ let mode = metadata.mode();
Ok(JoshutoMetadata {
len,
@@ -38,6 +42,8 @@ impl JoshutoMetadata {
uid,
#[cfg(unix)]
gid,
+ #[cfg(unix)]
+ mode,
})
}
}
diff --git a/src/main.rs b/src/main.rs
index d50927e..a2a01be 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -9,9 +9,7 @@ mod run;
mod sort;
mod tab;
mod ui;
-mod unix;
mod util;
-mod window;
use lazy_static::lazy_static;
use std::path::PathBuf;
diff --git a/src/run.rs b/src/run.rs
index 16af05d..c532941 100644
--- a/src/run.rs
+++ b/src/run.rs
@@ -2,7 +2,7 @@ use std::thread;
use crate::commands::{CommandKeybind, CursorMoveUp, JoshutoRunnable};
use crate::config::{JoshutoCommandMapping, JoshutoConfig};
-use crate::context::JoshutoContext;
+use crate::context::{JoshutoContext, MESSAGE_VISIBLE_DURATION};
use crate::tab::JoshutoTab;
use crate::ui;
use crate::ui::widgets::{TuiCommandMenu, TuiView};
@@ -56,9 +56,7 @@ pub fn run(config_t: JoshutoConfig, keymap_t: JoshutoCommandMapping) -> std::io:
Ok(event) => {
match event {
Event::IOWorkerProgress(p) => {
- context
- .message_queue
- .push_back(format!("bytes copied {}", p));
+ context.worker_msg = Some(format!("bytes copied {}", p));
}
Event::IOWorkerResult => {
match io_handle {
@@ -71,33 +69,45 @@ pub fn run(config_t: JoshutoConfig, keymap_t: JoshutoCommandMapping) -> std::io:
None => {}
}
io_handle = None;
+ context.worker_msg = None;
}
- Event::Input(key) => match keymap_t.get(&key) {
- None => {
- context
- .message_queue
- .push_back(format!("Unknown keycode: {:?}", key));
- }
- Some(CommandKeybind::SimpleKeybind(command)) => {
- if let Err(e) = command.execute(&mut context, &mut backend) {
- context.message_queue.push_back(e.to_string());
+ Event::Input(key) => {
+ /* Message handling */
+ if !context.message_queue.is_empty() {
+ if context.message_elapse < MESSAGE_VISIBLE_DURATION {
+ context.message_elapse += 1;
+ } else {
+ let _ = context.message_queue.pop_front();
+ context.message_elapse = 0;
}
}
- Some(CommandKeybind::CompositeKeybind(m)) => {
- let mut map: &JoshutoCommandMapping = &m;
-
- let cmd = {
- let mut menu = TuiCommandMenu::new();
- menu.get_input(&mut backend, &context, map)
- };
-
- if let Some(command) = cmd {
+ match keymap_t.get(&key) {
+ None => {
+ context
+ .message_queue
+ .push_back(format!("Unknown keycode: {:?}", key));
+ }
+ Some(CommandKeybind::SimpleKeybind(command)) => {
if let Err(e) = command.execute(&mut context, &mut backend) {
context.message_queue.push_back(e.to_string());
}
}
+ Some(CommandKeybind::CompositeKeybind(m)) => {
+ let mut map: &JoshutoCommandMapping = &m;
+
+ let cmd = {
+ let mut menu = TuiCommandMenu::new();
+ menu.get_input(&mut backend, &context, map)
+ };
+
+ if let Some(command) = cmd {
+ if let Err(e) = command.execute(&mut context, &mut backend) {
+ context.message_queue.push_back(e.to_string());
+ }
+ }
+ }
}
- },
+ }
}
let mut view = TuiView::new(&context);
backend.render(&mut view);
diff --git a/src/ui/widgets/mod.rs b/src/ui/widgets/mod.rs
index e36c904..49fbfc5 100644
--- a/src/ui/widgets/mod.rs
+++ b/src/ui/widgets/mod.rs
@@ -2,6 +2,7 @@ pub mod tui_dirlist;
pub mod tui_dirlist_detailed;
pub mod tui_footer;
pub mod tui_menu;
+pub mod tui_prompt;
pub mod tui_textfield;
pub mod tui_topbar;
pub mod tui_view;
@@ -10,6 +11,7 @@ pub use self::tui_dirlist::TuiDirList;
pub use self::tui_dirlist_detailed::TuiDirListDetailed;
pub use self::tui_footer::TuiFooter;
pub use self::tui_menu::{TuiCommandMenu, TuiMenu};
+pub use self::tui_prompt::TuiPrompt;
pub use self::tui_textfield::TuiTextField;
pub use self::tui_topbar::TuiTopBar;
pub use self::tui_view::TuiView;
diff --git a/src/ui/widgets/tui_prompt.rs b/src/ui/widgets/tui_prompt.rs
new file mode 100644
index 0000000..21526f3
--- /dev/null
+++ b/src/ui/widgets/tui_prompt.rs
@@ -0,0 +1,60 @@
+use termion::event::Key;
+use tui::layout::Rect;
+use tui::style::{Color, Style};
+use tui::widgets::{Block, Borders, List, Paragraph, Text, Widget};
+use unicode_width::UnicodeWidthStr;
+
+use crate::context::JoshutoContext;
+use crate::ui::TuiBackend;
+use crate::util::event::{Event, Events};
+
+use super::TuiView;
+
+pub struct TuiPrompt<'a> {
+ prompt: &'a str,
+}
+
+impl<'a> TuiPrompt<'a> {
+ pub fn new(prompt: &'a str) -> Self {
+ Self { prompt }
+ }
+
+ pub fn get_key(&mut self, backend: &mut TuiBackend, context: &JoshutoContext) -> Key {
+ loop {
+ backend.terminal.draw(|mut frame| {
+ let f_size = frame.size();
+ if f_size.height == 0 {
+ return;
+ }
+
+ {
+ let mut view = TuiView::new(&context);
+ view.show_bottom_status = false;
+ view.render(&mut frame, f_size);
+ }
+
+ let prompt_style = Style::default().fg(Color::LightYellow);
+
+ let text = [Text::styled(self.prompt, prompt_style)];
+
+ let textfield_rect = Rect {
+ x: 0,
+ y: f_size.height - 1,
+ width: f_size.width,
+ height: 1,
+ };
+
+ Paragraph::new(text.iter())
+ .wrap(true)
+ .render(&mut frame, textfield_rect);
+ });
+
+ if let Ok(event) = context.events.next() {
+ match event {
+ Event::Input(key) => return key,
+ _ => {}
+ };
+ }
+ }
+ }
+}
diff --git a/src/ui/widgets/tui_textfield.rs b/src/ui/widgets/tui_textfield.rs
index c0f54a8..9dbbad3 100644
--- a/src/ui/widgets/tui_textfield.rs
+++ b/src/ui/widgets/tui_textfield.rs
@@ -1,9 +1,11 @@
+use std::io::Write;
+
use rustyline::completion::{Candidate, Completer, FilenameCompleter, Pair};
use rustyline::line_buffer;
-use termion::clear;
use termion::cursor::Goto;
use termion::event::Key;
+use tui::backend::Backend;
use tui::layout::Rect;
use tui::style::{Color, Style};
use tui::widgets::{Block, Borders, List, Paragraph, Text, Widget};
@@ -13,7 +15,7 @@ use crate::context::JoshutoContext;
use crate::ui::TuiBackend;
use crate::util::event::{Event, Events};
-use super::TuiMenu;
+use super::{TuiMenu, TuiView};
struct CompletionTracker {
pub index: usize,
@@ -34,12 +36,31 @@ impl CompletionTracker {
}
pub struct TuiTextField<'a> {
- menu: Option<&'a mut TuiMenu<'a>>,
+ _prompt: &'a str,
+ _prefix: &'a str,
+ _suffix: &'a str,
+ _menu: Option<&'a mut TuiMenu<'a>>,
}
impl<'a> TuiTextField<'a> {
- pub fn new(menu: &'a mut TuiMenu<'a>) -> Self {
- Self { menu: Some(menu) }
+ pub fn menu(mut self, menu: &'a mut TuiMenu<'a>) -> Self {
+ self._menu = Some(menu);
+ self
+ }
+
+ pub fn prompt(mut self, prompt: &'a str) -> Self {
+ self._prompt = prompt;
+ self
+ }
+
+ pub fn prefix(mut self, prefix: &'a str) -> Self {
+ self._prefix = prefix;
+ self
+ }
+
+ pub fn suffix(mut self, suffix: &'a str) -> Self {
+ self._suffix = suffix;
+ self
}
pub fn get_input(
@@ -47,11 +68,44 @@ impl<'a> TuiTextField<'a> {
backend: &mut TuiBackend,
context: &JoshutoContext,
) -> Option<String> {
- let mut input_string = String::with_capacity(64);
+ let mut line_buffer = line_buffer::LineBuffer::with_capacity(255);
+ let completer = FilenameCompleter::new();
+
+ let mut completion_tracker: Option<CompletionTracker> = None;
+
+ let mut char_idx = self
+ ._prefix
+ .char_indices()
+ .last()
+ .map(|(i, c)| i)
+ .unwrap_or(0);
+
+ line_buffer.insert_str(0, self._prefix);
+ line_buffer.insert_str(line_buffer.len(), self._suffix);
+ line_buffer.set_pos(char_idx);
+
+ backend.terminal.show_cursor();
+ let mut cursor_xpos = line_buffer.pos() + 1;
+ {
+ let frame = backend.terminal.get_frame();
+ let f_size = frame.size();
+ backend
+ .terminal
+ .set_cursor(cursor_xpos as u16, f_size.height - 1);
+ }
loop {
backend.terminal.draw(|mut frame| {
let f_size = frame.size();
+ if f_size.height == 0 {
+ return;
+ }
+
+ {
+ let mut view = TuiView::new(&context);
+ view.show_bottom_status = false;
+ view.render(&mut frame, f_size);
+ }
let top_rect = Rect {
x: 0,
@@ -60,27 +114,129 @@ impl<'a> TuiTextField<'a> {
height: 1,
};
- if let Some(menu) = self.menu.as_mut() {
+ if let Some(menu) = self._menu.as_mut() {
menu.render(&mut frame, top_rect);
}
+
+ let cmd_prompt_style = Style::default().fg(Color::LightGreen);
+
+ let text = [
+ Text::styled(self._prompt, cmd_prompt_style),
+ Text::raw(line_buffer.as_str()),
+ ];
+
+ let textfield_rect = Rect {
+ x: 0,
+ y: f_size.height - 1,
+ width: f_size.width,
+ height: 1,
+ };
+
+ Paragraph::new(text.iter())
+ .wrap(true)
+ .render(&mut frame, textfield_rect);
});
if let Ok(event) = context.events.next() {
match event {
+ Event::Input(Key::Backspace) => {
+ if line_buffer.backspace(1) {
+ completion_tracker.take();
+ }
+ }
+ Event::Input(Key::Left) => {
+ if line_buffer.move_backward(1) {
+ completion_tracker.take();
+ }
+ }
+ Event::Input(Key::Right) => {
+ if line_buffer.move_forward(1) {
+ completion_tracker.take();
+ }
+ }
+ Event::Input(Key::Delete) => {
+ if line_buffer.delete(1).is_some() {
+ completion_tracker.take();
+ }
+ }
+ Event::Input(Key::Home) => {
+ line_buffer.move_end();
+ completion_tracker.take();
+ }
+ Event::Input(Key::End) => {
+ line_buffer.move_end();
+ completion_tracker.take();
+ }
+ Event::Input(Key::Up) => {}
+ Event::Input(Key::Down) => {}
Event::Input(Key::Esc) => {
+ backend.terminal.hide_cursor();
return None;
}
+ Event::Input(Key::Char('\t')) => {
+ if completion_tracker.is_none() {
+ let res =
+ completer.complete_path(line_buffer.as_str(), line_buffer.pos());
+ if let Ok((pos, mut candidates)) = res {
+ candidates.sort_by(|x, y| {
+ x.display()
+ .partial_cmp(y.display())
+ .unwrap_or(std::cmp::Ordering::Less)
+ });
+ let ct = CompletionTracker::new(
+ pos,
+ candidates,
+ String::from(line_buffer.as_str()),
+ );
+ completion_tracker = Some(ct);
+ }
+ }
+
+ if let Some(ref mut s) = completion_tracker {
+ if s.index < s.candidates.len() {
+ let candidate = &s.candidates[s.index];
+ completer.update(&mut line_buffer, s.pos, candidate.replacement());
+ s.index += 1;
+ }
+ }
+ }
Event::Input(Key::Char('\n')) => {
break;
}
Event::Input(Key::Char(c)) => {
- input_string.push(c);
+ if line_buffer.insert(c, 1).is_some() {
+ completion_tracker.take();
+ }
}
_ => {}
};
}
+ cursor_xpos = line_buffer.pos() + 1;
+ {
+ let frame = backend.terminal.get_frame();
+ let f_size = frame.size();
+ backend
+ .terminal
+ .set_cursor(cursor_xpos as u16, f_size.height - 1);
+ }
+ }
+ backend.terminal.hide_cursor();
+ if line_buffer.as_str().is_empty() {
+ None
+ } else {
+ let strin = line_buffer.to_string();
+ Some(strin)
+ }
+ }
+}
+
+impl<'a> std::default::Default for TuiTextField<'a> {
+ fn default() -> Self {
+ Self {
+ _prompt: "",
+ _prefix: "",
+ _suffix: "",
+ _menu: None,
}
- eprintln!("You typed: {}", input_string);
- Some(input_string)
}
}
diff --git a/src/ui/widgets/tui_view.rs b/src/ui/widgets/tui_view.rs
index 57f644c..0f2d64d 100644
--- a/src/ui/widgets/tui_view.rs
+++ b/src/ui/widgets/tui_view.rs
@@ -1,6 +1,7 @@
use tui::buffer::Buffer;
use tui::layout::{Direction, Layout, Rect};
-use tui::widgets::Widget;
+use tui::style::{Color, Modifier, Style};
+use tui::widgets::{Paragraph, Text, Widget};
use unicode_width::UnicodeWidthStr;
use super::{TuiDirList, TuiDirListDetailed, TuiFooter, TuiTopBar};
@@ -8,13 +9,17 @@ use crate::context::JoshutoContext;
pub struct TuiView<'a> {
pub context: &'a JoshutoContext,
+ pub show_bottom_status: bool,
}
use super::super::{DEFAULT_LAYOUT, NO_PREVIEW_LAYOUT};
impl<'a> TuiView<'a> {
pub fn new(context: &'a JoshutoContext) -> Self {
- Self { context }
+ Self {
+ context,
+ show_bottom_status: true,
+ }
}
}
@@ -55,15 +60,30 @@ impl<'a> Widget for TuiView<'a> {
if let Some(curr_list) = curr_list.as_ref() {
TuiDirListDetailed::new(&curr_list).draw(layout_rect[1], buf);
+ let rect = Rect {
+ x: 0,
+ y: f_size.height - 1,
+ width: f_size.width,
+ height: 1,
+ };
+
+ let message_style = Style::default()
+ .fg(Color::LightCyan)
+ .modifier(Modifier::BOLD);
+
+ if self.show_bottom_status {
+ /* draw the bottom status bar */
+ if let Some(msg) = self.context.worker_msg.as_ref() {
+ let text = [Text::styled(msg, message_style)];
+
+ Paragraph::new(text.iter()).wrap(true).draw(rect, buf);
+ } else if !self.context.message_queue.is_empty() {
+ let text = [Text::styled(&self.context.message_queue[0], message_style)];
- if let Some(entry) = curr_list.get_curr_ref() {
- let rect = Rect {
- x: 0,
- y: f_size.height - 1,
- width: f_size.width,
- height: 1,
- };
- TuiFooter::new(entry).draw(rect, buf);
+ Paragraph::new(text.iter()).wrap(true).draw(rect, buf);
+ } else if let Some(entry) = curr_list.get_curr_ref() {
+ TuiFooter::new(entry).draw(rect, buf);
+ }
}
};
diff --git a/src/util/format.rs b/src/util/format.rs
index 144aaaf..53fbf5f 100644
--- a/src/util/format.rs
+++ b/src/util/format.rs
@@ -1,6 +1,6 @@
use std::time;
-use crate::unix;
+use super::unix;
pub fn file_size_to_string(mut file_size: f64) -> String {
const FILE_UNITS: [&str; 6] = ["B", "K", "M", "G", "T", "E"];
diff --git a/src/util/mod.rs b/src/util/mod.rs
index 1ec901b..9f0a3b6 100644
--- a/src/util/mod.rs
+++ b/src/util/mod.rs
@@ -2,3 +2,4 @@ pub mod event;
pub mod format;
pub mod key_mapping;
pub mod load_child;
+pub mod unix;
diff --git a/src/unix.rs b/src/util/unix.rs
index 1c25cd2..2113fc1 100644
--- a/src/unix.rs
+++ b/src/util/unix.rs
@@ -3,10 +3,7 @@ use std::path::Path;
pub fn is_executable(mode: u32) -> bool {
const LIBC_PERMISSION_VALS: [libc::mode_t; 3] = [libc::S_IXUSR, libc::S_IXGRP, libc::S_IXOTH];
- LIBC_PERMISSION_VALS.iter().any(|val| {
- let val: u32 = (*val) as u32;
- mode & val != 0
- })
+ LIBC_PERMISSION_VALS.iter().any(|val| mode & *val != 0)
}
pub fn stringify_mode(mode: u32) -> String {