summaryrefslogtreecommitdiffstats
path: root/src/ui
diff options
context:
space:
mode:
authorJiayi Zhao <jeff.no.zhao@gmail.com>2020-08-30 15:20:03 -0400
committerJiayi Zhao <jeff.no.zhao@gmail.com>2020-08-30 15:20:03 -0400
commite7218c81d90ae07d7f56dce0c3032db15b11d118 (patch)
tree63c3b63345ec73031b26c1b900957ad06b69aec6 /src/ui
parenta592bfe51c0cbb7744f14586520827cb06da8c8d (diff)
rework and fix issues
- fixed bug where io tasks would not run when user is in a textfield or prompt - fixed bug where cut doesn't work - rework structs to have private fields and public functions - move IOWorkerObserver into seperate file - move code from TuiView to TuiFolderView
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/widgets/mod.rs22
-rw-r--r--src/ui/widgets/tui_folder_view.rs129
-rw-r--r--src/ui/widgets/tui_menu.rs39
-rw-r--r--src/ui/widgets/tui_prompt.rs17
-rw-r--r--src/ui/widgets/tui_tab.rs3
-rw-r--r--src/ui/widgets/tui_textfield.rs52
-rw-r--r--src/ui/widgets/tui_view.rs104
-rw-r--r--src/ui/widgets/tui_worker_view.rs79
8 files changed, 289 insertions, 156 deletions
diff --git a/src/ui/widgets/mod.rs b/src/ui/widgets/mod.rs
index 2600255..fe756a3 100644
--- a/src/ui/widgets/mod.rs
+++ b/src/ui/widgets/mod.rs
@@ -1,15 +1,18 @@
-pub mod tui_dirlist;
-pub mod tui_dirlist_detailed;
-pub mod tui_footer;
-pub mod tui_menu;
-pub mod tui_prompt;
-pub mod tui_tab;
-pub mod tui_textfield;
-pub mod tui_topbar;
-pub mod tui_view;
+mod tui_dirlist;
+mod tui_dirlist_detailed;
+mod tui_folder_view;
+mod tui_footer;
+mod tui_menu;
+mod tui_prompt;
+mod tui_tab;
+mod tui_textfield;
+mod tui_topbar;
+mod tui_view;
+mod tui_worker_view;
pub use self::tui_dirlist::TuiDirList;
pub use self::tui_dirlist_detailed::TuiDirListDetailed;
+pub use self::tui_folder_view::TuiFolderView;
pub use self::tui_footer::TuiFooter;
pub use self::tui_menu::{TuiCommandMenu, TuiMenu};
pub use self::tui_prompt::TuiPrompt;
@@ -17,3 +20,4 @@ pub use self::tui_tab::TuiTabBar;
pub use self::tui_textfield::TuiTextField;
pub use self::tui_topbar::TuiTopBar;
pub use self::tui_view::TuiView;
+pub use self::tui_worker_view::TuiWorkerView;
diff --git a/src/ui/widgets/tui_folder_view.rs b/src/ui/widgets/tui_folder_view.rs
new file mode 100644
index 0000000..6c145dd
--- /dev/null
+++ b/src/ui/widgets/tui_folder_view.rs
@@ -0,0 +1,129 @@
+use tui::buffer::Buffer;
+use tui::layout::{Direction, Layout, Rect};
+use tui::style::{Color, Style};
+use tui::text::Span;
+use tui::widgets::{Paragraph, Widget, Wrap};
+
+use super::{TuiDirList, TuiDirListDetailed, TuiFooter, TuiTabBar, TuiTopBar};
+use crate::context::JoshutoContext;
+
+const TAB_VIEW_WIDTH: u16 = 15;
+
+pub struct TuiFolderView<'a> {
+ pub context: &'a JoshutoContext,
+ pub show_bottom_status: bool,
+}
+
+use super::super::{DEFAULT_LAYOUT, NO_PREVIEW_LAYOUT};
+
+impl<'a> TuiFolderView<'a> {
+ pub fn new(context: &'a JoshutoContext) -> Self {
+ Self {
+ context,
+ show_bottom_status: true,
+ }
+ }
+}
+
+impl<'a> Widget for TuiFolderView<'a> {
+ fn render(self, area: Rect, buf: &mut Buffer) {
+ let f_size = area;
+
+ let curr_tab = self.context.tab_context_ref().curr_tab_ref();
+
+ let curr_list = curr_tab.curr_list_ref();
+ let parent_list = curr_tab.parent_list_ref();
+ let child_list = curr_tab.child_list_ref();
+
+ let constraints = match child_list {
+ Some(_) => DEFAULT_LAYOUT,
+ None => NO_PREVIEW_LAYOUT,
+ };
+ let layout_rect = Layout::default()
+ .direction(Direction::Horizontal)
+ .margin(1)
+ .constraints(constraints.as_ref())
+ .split(f_size);
+
+ if self.context.tab_context_ref().len() > 1 {
+ let topbar_width = if f_size.width > TAB_VIEW_WIDTH {
+ f_size.width - TAB_VIEW_WIDTH
+ } else {
+ 0
+ };
+
+ let rect = Rect {
+ x: 0,
+ y: 0,
+ width: topbar_width,
+ height: 1,
+ };
+ TuiTopBar::new(curr_tab.pwd()).render(rect, buf);
+
+ let rect = Rect {
+ x: topbar_width,
+ y: 0,
+ width: TAB_VIEW_WIDTH,
+ height: 1,
+ };
+ let name = if let Some(ostr) = curr_tab.pwd().file_name() {
+ ostr.to_str().unwrap_or("")
+ } else {
+ ""
+ };
+ TuiTabBar::new(
+ name,
+ self.context.tab_context_ref().get_index(),
+ self.context.tab_context_ref().len(),
+ )
+ .render(rect, buf);
+ } else {
+ let topbar_width = f_size.width;
+
+ let rect = Rect {
+ x: 0,
+ y: 0,
+ width: topbar_width,
+ height: 1,
+ };
+ TuiTopBar::new(curr_tab.pwd()).render(rect, buf);
+ }
+
+ if let Some(list) = parent_list.as_ref() {
+ TuiDirList::new(&list).render(layout_rect[0], buf);
+ };
+
+ if let Some(list) = curr_list.as_ref() {
+ TuiDirListDetailed::new(&list).render(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::Yellow);
+
+ if self.show_bottom_status {
+ /* draw the bottom status bar */
+ if let Some(msg) = self.context.worker_msg() {
+ let text = Span::styled(msg.as_str(), message_style);
+ Paragraph::new(text)
+ .wrap(Wrap { trim: true })
+ .render(rect, buf);
+ } else if !self.context.message_queue_ref().is_empty() {
+ let text = Span::styled(&self.context.message_queue_ref()[0], message_style);
+ Paragraph::new(text)
+ .wrap(Wrap { trim: true })
+ .render(rect, buf);
+ } else if let Some(entry) = list.get_curr_ref() {
+ TuiFooter::new(entry).render(rect, buf);
+ }
+ }
+ };
+
+ if let Some(list) = child_list.as_ref() {
+ TuiDirList::new(&list).render(layout_rect[2], buf);
+ };
+ }
+}
diff --git a/src/ui/widgets/tui_menu.rs b/src/ui/widgets/tui_menu.rs
index 09c1f75..e37c28f 100644
--- a/src/ui/widgets/tui_menu.rs
+++ b/src/ui/widgets/tui_menu.rs
@@ -3,8 +3,8 @@ use std::iter::Iterator;
use termion::event::Key;
use tui::buffer::Buffer;
use tui::layout::Rect;
-use tui::style::Style;
-use tui::widgets::{Block, Borders, Widget};
+use tui::style::{Color, Style};
+use tui::widgets::{Block, Borders, Clear, Widget};
use unicode_width::UnicodeWidthStr;
use super::TuiView;
@@ -13,6 +13,7 @@ use crate::config::JoshutoCommandMapping;
use crate::context::JoshutoContext;
use crate::ui::TuiBackend;
use crate::util::event::Event;
+use crate::util::worker;
const BORDER_HEIGHT: usize = 1;
const BOTTOM_MARGIN: usize = 1;
@@ -27,17 +28,18 @@ impl TuiCommandMenu {
pub fn get_input<'a>(
&mut self,
backend: &mut TuiBackend,
- context: &JoshutoContext,
+ context: &mut JoshutoContext,
m: &'a JoshutoCommandMapping,
) -> Option<&'a Box<dyn JoshutoCommand>> {
let mut map: &JoshutoCommandMapping = &m;
let terminal = backend.terminal_mut();
- context.events.flush();
+ context.flush_event();
loop {
terminal.draw(|frame| {
let f_size: Rect = frame.size();
+
{
let view = TuiView::new(&context);
frame.render_widget(view, f_size);
@@ -72,12 +74,19 @@ impl TuiCommandMenu {
height: (display_str_len + BORDER_HEIGHT) as u16,
};
+ frame.render_widget(Clear, menu_rect);
frame.render_widget(TuiMenu::new(&display_str), menu_rect);
}
});
- if let Ok(event) = context.events.next() {
+ if let Ok(event) = context.poll_event() {
match event {
+ Event::IOWorkerProgress(res) => {
+ worker::process_worker_progress(context, res);
+ }
+ Event::IOWorkerResult(res) => {
+ worker::process_finished_worker(context, res);
+ }
Event::Input(key) => {
match key {
Key::Esc => return None,
@@ -91,9 +100,8 @@ impl TuiCommandMenu {
None => return None,
},
}
- context.events.flush();
+ context.flush_event();
}
- _ => {}
}
}
}
@@ -114,27 +122,22 @@ impl<'a> TuiMenu<'a> {
}
}
-const LONG_SPACE: &str = " ";
impl<'a> Widget for TuiMenu<'a> {
fn render(self, area: Rect, buf: &mut Buffer) {
- let text_iter = self.options.iter();
- let style = Style::default();
+ let text_iter = self.options.iter().chain(&[" "]);
+ let style = Style::default().fg(Color::Reset).bg(Color::Reset);
let area_x = area.x + 1;
let area_y = area.y + 1;
- Block::default().borders(Borders::TOP).render(area, buf);
+ Block::default()
+ .style(style)
+ .borders(Borders::TOP)
+ .render(area, buf);
for (i, text) in text_iter.enumerate() {
let width = text.width();
buf.set_stringn(area_x, area_y + i as u16, text, width, style);
- buf.set_stringn(
- area_x + width as u16,
- area_y + i as u16,
- LONG_SPACE,
- area.width as usize,
- style,
- );
}
}
}
diff --git a/src/ui/widgets/tui_prompt.rs b/src/ui/widgets/tui_prompt.rs
index 2da3ba9..112db21 100644
--- a/src/ui/widgets/tui_prompt.rs
+++ b/src/ui/widgets/tui_prompt.rs
@@ -7,6 +7,7 @@ use tui::widgets::{Paragraph, Wrap};
use crate::context::JoshutoContext;
use crate::ui::TuiBackend;
use crate::util::event::Event;
+use crate::util::worker;
use super::TuiView;
@@ -19,10 +20,10 @@ impl<'a> TuiPrompt<'a> {
Self { prompt }
}
- pub fn get_key(&mut self, backend: &mut TuiBackend, context: &JoshutoContext) -> Key {
+ pub fn get_key(&mut self, backend: &mut TuiBackend, context: &mut JoshutoContext) -> Key {
let terminal = backend.terminal_mut();
- context.events.flush();
+ context.flush_event();
loop {
terminal.draw(|frame| {
let f_size: Rect = frame.size();
@@ -49,15 +50,21 @@ impl<'a> TuiPrompt<'a> {
frame.render_widget(
Paragraph::new(text).wrap(Wrap { trim: true }),
- textfield_rect);
+ textfield_rect,
+ );
});
- if let Ok(event) = context.events.next() {
+ if let Ok(event) = context.poll_event() {
match event {
+ Event::IOWorkerProgress(res) => {
+ worker::process_worker_progress(context, res);
+ }
+ Event::IOWorkerResult(res) => {
+ worker::process_finished_worker(context, res);
+ }
Event::Input(key) => {
return key;
}
- _ => {}
};
}
}
diff --git a/src/ui/widgets/tui_tab.rs b/src/ui/widgets/tui_tab.rs
index 76fb6e7..85a3b8a 100644
--- a/src/ui/widgets/tui_tab.rs
+++ b/src/ui/widgets/tui_tab.rs
@@ -18,8 +18,7 @@ impl<'a> TuiTabBar<'a> {
impl<'a> Widget for TuiTabBar<'a> {
fn render(self, area: Rect, buf: &mut Buffer) {
- let selected = Style::default()
- .add_modifier(Modifier::REVERSED);
+ let selected = Style::default().add_modifier(Modifier::REVERSED);
let text = Spans::from(vec![
Span::styled(format!("{}: {}", self.curr + 1, self.name), selected),
diff --git a/src/ui/widgets/tui_textfield.rs b/src/ui/widgets/tui_textfield.rs
index 08696e6..3ca7ad9 100644
--- a/src/ui/widgets/tui_textfield.rs
+++ b/src/ui/widgets/tui_textfield.rs
@@ -5,11 +5,12 @@ use termion::event::Key;
use tui::layout::Rect;
use tui::style::{Color, Modifier, Style};
use tui::text::{Span, Spans};
-use tui::widgets::{Paragraph, Wrap};
+use tui::widgets::{Clear, Paragraph, Wrap};
use crate::context::JoshutoContext;
use crate::ui::TuiBackend;
use crate::util::event::Event;
+use crate::util::worker;
use super::{TuiMenu, TuiView};
@@ -35,12 +36,13 @@ pub struct TuiTextField<'a> {
_prompt: &'a str,
_prefix: &'a str,
_suffix: &'a str,
- _menu: Option<TuiMenu<'a>>,
+ _menu_items: Option<Vec<&'a str>>,
}
impl<'a> TuiTextField<'a> {
- pub fn menu(&mut self, menu: TuiMenu<'a>) -> &mut Self {
- self._menu = Some(menu);
+ pub fn menu_items<I>(&mut self, items: I) -> &mut Self
+ where I: Iterator<Item = &'a str> {
+ self._menu_items = Some(items.collect());
self
}
@@ -62,9 +64,9 @@ impl<'a> TuiTextField<'a> {
pub fn get_input(
&mut self,
backend: &mut TuiBackend,
- context: &JoshutoContext,
+ context: &mut JoshutoContext,
) -> Option<String> {
- context.events.flush();
+ context.flush_event();
let mut line_buffer = line_buffer::LineBuffer::with_capacity(255);
let completer = FilenameCompleter::new();
@@ -93,8 +95,8 @@ impl<'a> TuiTextField<'a> {
frame.render_widget(view, f_size);
}
- if let Some(menu) = self._menu.take() {
- let menu_len = menu.len();
+ if let Some(items) = self._menu_items.as_ref() {
+ let menu_len = items.len();
let menu_y = if menu_len + 2 > f_size.height as usize {
0
} else {
@@ -107,15 +109,12 @@ impl<'a> TuiTextField<'a> {
width: f_size.width,
height: menu_len as u16,
};
- frame.render_widget(menu, rect);
+ let menu_widget = TuiMenu::new(items);
+ frame.render_widget(menu_widget, rect);
}
let cursor_xpos = line_buffer.pos();
- let cmd_prompt_style = Style::default().fg(Color::LightGreen);
- let cursor_style = Style::default()
- .add_modifier(Modifier::REVERSED);
-
let prefix = &line_buffer.as_str()[..cursor_xpos];
let curr = line_buffer.as_str()[cursor_xpos..].chars().next();
@@ -127,13 +126,19 @@ impl<'a> TuiTextField<'a> {
None => ("", ' '),
};
+ let cmd_prompt_style = Style::default().fg(Color::LightGreen);
+ let cursor_style = Style::default().add_modifier(Modifier::REVERSED);
+ let default_style = Style::default().fg(Color::Reset).bg(Color::Reset);
+
let curr_string = curr.to_string();
let text = Spans::from(vec![
Span::styled(self._prompt, cmd_prompt_style),
- Span::raw(prefix),
+ Span::styled(prefix, default_style),
Span::styled(curr_string, cursor_style),
- Span::raw(suffix),
+ Span::styled(suffix, default_style),
+ Span::styled(" ", default_style),
+ Span::styled(".", Style::default().fg(Color::Black).bg(Color::Reset)),
]);
let textfield_rect = Rect {
@@ -143,14 +148,22 @@ impl<'a> TuiTextField<'a> {
height: 1,
};
+ frame.render_widget(Clear, textfield_rect);
frame.render_widget(
Paragraph::new(text).wrap(Wrap { trim: true }),
- textfield_rect);
+ textfield_rect,
+ );
})
.unwrap();
- if let Ok(event) = context.events.next() {
+ if let Ok(event) = context.poll_event() {
match event {
+ Event::IOWorkerProgress(res) => {
+ worker::process_worker_progress(context, res);
+ }
+ Event::IOWorkerResult(res) => {
+ worker::process_finished_worker(context, res);
+ }
Event::Input(key) => {
match key {
Key::Backspace => {
@@ -227,9 +240,8 @@ impl<'a> TuiTextField<'a> {
}
_ => {}
}
- context.events.flush();
+ context.flush_event();
}
- _ => {}
};
}
}
@@ -248,7 +260,7 @@ impl<'a> std::default::Default for TuiTextField<'a> {
_prompt: "",
_prefix: "",
_suffix: "",
- _menu: None,
+ _menu_items: None,
}
}
}
diff --git a/src/ui/widgets/tui_view.rs b/src/ui/widgets/tui_view.rs
index 48345bc..29a8122 100644
--- a/src/ui/widgets/tui_view.rs
+++ b/src/ui/widgets/tui_view.rs
@@ -4,7 +4,7 @@ use tui::style::{Color, Style};
use tui::text::Span;
use tui::widgets::{Paragraph, Widget, Wrap};
-use super::{TuiDirList, TuiDirListDetailed, TuiFooter, TuiTabBar, TuiTopBar};
+use super::TuiFolderView;
use crate::context::JoshutoContext;
const TAB_VIEW_WIDTH: u16 = 15;
@@ -27,106 +27,6 @@ impl<'a> TuiView<'a> {
impl<'a> Widget for TuiView<'a> {
fn render(self, area: Rect, buf: &mut Buffer) {
- let f_size = area;
-
- let curr_tab = self.context.tab_context_ref().curr_tab_ref();
-
- let curr_list = curr_tab.curr_list_ref();
- let parent_list = curr_tab.parent_list_ref();
- let child_list = curr_tab.child_list_ref();
-
- let constraints = match child_list {
- Some(_) => DEFAULT_LAYOUT,
- None => NO_PREVIEW_LAYOUT,
- };
- let layout_rect = Layout::default()
- .direction(Direction::Horizontal)
- .margin(1)
- .constraints(constraints.as_ref())
- .split(f_size);
-
- {
- if self.context.tab_context_ref().len() > 1 {
- let topbar_width = if f_size.width > TAB_VIEW_WIDTH {
- f_size.width - TAB_VIEW_WIDTH
- } else {
- 0
- };
-
- let rect = Rect {
- x: 0,
- y: 0,
- width: topbar_width,
- height: 1,
- };
- TuiTopBar::new(curr_tab.pwd()).render(rect, buf);
-
- let rect = Rect {
- x: topbar_width,
- y: 0,
- width: TAB_VIEW_WIDTH,
- height: 1,
- };
- let name = if let Some(ostr) = curr_tab.pwd().file_name() {
- ostr.to_str().unwrap_or("")
- } else {
- ""
- };
- TuiTabBar::new(
- name,
- self.context.tab_context_ref().get_index(),
- self.context.tab_context_ref().len(),
- )
- .render(rect, buf);
- } else {
- let topbar_width = f_size.width;
-
- let rect = Rect {
- x: 0,
- y: 0,
- width: topbar_width,
- height: 1,
- };
- TuiTopBar::new(curr_tab.pwd()).render(rect, buf);
- }
- }
-
- if let Some(curr_list) = parent_list.as_ref() {
- TuiDirList::new(&curr_list).render(layout_rect[0], buf);
- };
-
- if let Some(curr_list) = curr_list.as_ref() {
- TuiDirListDetailed::new(&curr_list).render(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::Yellow);
-
- if self.show_bottom_status {
- /* draw the bottom status bar */
- if let Some(msg) = self.context.worker_msg() {
- let text = Span::styled(msg.as_str(), message_style);
- Paragraph::new(text)
- .wrap(Wrap { trim: true })
- .render(rect, buf);
- } else if !self.context.message_queue.is_empty() {
- let text = Span::styled(&self.context.message_queue[0],
- message_style);
- Paragraph::new(text)
- .wrap(Wrap { trim: true })
- .render(rect, buf);
- } else if let Some(entry) = curr_list.get_curr_ref() {
- TuiFooter::new(entry).render(rect, buf);
- }
- }
- };
-
- if let Some(curr_list) = child_list.as_ref() {
- TuiDirList::new(&curr_list).render(layout_rect[2], buf);
- };
+ TuiFolderView::new(self.context).render(area, buf);
}
}
diff --git a/src/ui/widgets/tui_worker_view.rs b/src/ui/widgets/tui_worker_view.rs
new file mode 100644
index 0000000..d17f7c2
--- /dev/null
+++ b/src/ui/widgets/tui_worker_view.rs
@@ -0,0 +1,79 @@
+use tui::buffer::Buffer;
+use tui::layout::{Direction, Layout, Rect};
+use tui::style::{Color, Style};
+use tui::text::Span;
+use tui::widgets::{Paragraph, Widget, Wrap};
+
+use super::{TuiDirList, TuiDirListDetailed, TuiFooter, TuiTabBar, TuiTopBar};
+use crate::context::JoshutoContext;
+
+const TAB_VIEW_WIDTH: u16 = 15;
+
+pub struct TuiWorkerView<'a> {
+ pub context: &'a JoshutoContext,
+ pub show_bottom_status: bool,
+}
+
+use super::super::{DEFAULT_LAYOUT, NO_PREVIEW_LAYOUT};
+
+impl<'a> TuiWorkerView<'a> {
+ pub fn new(context: &'a JoshutoContext) -> Self {
+ Self {
+ context,
+ show_bottom_status: true,
+ }
+ }
+}
+
+impl<'a> Widget for TuiWorkerView<'a> {
+ fn render(self, area: Rect, buf: &mut Buffer) {
+ let f_size = area;
+
+ let curr_tab = self.context.tab_context_ref().curr_tab_ref();
+ let layout_rect = Layout::default().direction(Direction::Horizontal).margin(1);
+
+ if self.context.tab_context_ref().len() > 1 {
+ let topbar_width = if f_size.width > TAB_VIEW_WIDTH {
+ f_size.width - TAB_VIEW_WIDTH
+ } else {
+ 0
+ };
+
+ let rect = Rect {
+ x: 0,
+ y: 0,
+ width: topbar_width,
+ height: 1,
+ };
+ TuiTopBar::new(curr_tab.pwd()).render(rect, buf);
+
+ let rect = Rect {
+ x: topbar_width,
+ y: 0,
+ width: TAB_VIEW_WIDTH,
+ height: 1,
+ };
+ let name = if let Some(ostr) = curr_tab.pwd().file_name() {
+ ostr.to_str().unwrap_or("")
+ } else {
+ ""
+ };
+ TuiTabBar::new(
+ name,
+ self.context.tab_context_ref().get_index(),
+ self.context.tab_context_ref().len(),
+ )
+ .render(rect, buf);
+ } else {
+ let topbar_width = f_size.width;
+
+ let rect = Rect {
+ x: 0,
+ y: 0,
+ width: topbar_width,
+ height: 1,
+ };
+ TuiTopBar::new(curr_tab.pwd()).render(rect, buf);
+ }
+ }
+}