diff options
author | Jiayi Zhao <jeff.no.zhao@gmail.com> | 2020-08-30 15:20:03 -0400 |
---|---|---|
committer | Jiayi Zhao <jeff.no.zhao@gmail.com> | 2020-08-30 15:20:03 -0400 |
commit | e7218c81d90ae07d7f56dce0c3032db15b11d118 (patch) | |
tree | 63c3b63345ec73031b26c1b900957ad06b69aec6 /src/ui | |
parent | a592bfe51c0cbb7744f14586520827cb06da8c8d (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.rs | 22 | ||||
-rw-r--r-- | src/ui/widgets/tui_folder_view.rs | 129 | ||||
-rw-r--r-- | src/ui/widgets/tui_menu.rs | 39 | ||||
-rw-r--r-- | src/ui/widgets/tui_prompt.rs | 17 | ||||
-rw-r--r-- | src/ui/widgets/tui_tab.rs | 3 | ||||
-rw-r--r-- | src/ui/widgets/tui_textfield.rs | 52 | ||||
-rw-r--r-- | src/ui/widgets/tui_view.rs | 104 | ||||
-rw-r--r-- | src/ui/widgets/tui_worker_view.rs | 79 |
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); + } + } +} |